mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-01-21 11:37:20 +01:00
Compare commits
22 Commits
shawn/fixe
...
dev/vanzue
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ecb37d3e9e | ||
|
|
3e58c10258 | ||
|
|
6b33c78555 | ||
|
|
335dcbaae8 | ||
|
|
6515985823 | ||
|
|
36a64cae90 | ||
|
|
30f832ac65 | ||
|
|
e0c0e7bb76 | ||
|
|
dfede67993 | ||
|
|
1438a628ad | ||
|
|
1b6b446915 | ||
|
|
78b0139bc3 | ||
|
|
709c4bbf6b | ||
|
|
bb3435322f | ||
|
|
d43e1d8cb2 | ||
|
|
1a145fd136 | ||
|
|
673cd5aba3 | ||
|
|
97997035f7 | ||
|
|
59962ffd3a | ||
|
|
3f106344b3 | ||
|
|
ab531b2620 | ||
|
|
48e95caf39 |
3
.github/actions/spell-check/allow/code.txt
vendored
3
.github/actions/spell-check/allow/code.txt
vendored
@@ -330,6 +330,9 @@ HHH
|
||||
riday
|
||||
YYY
|
||||
|
||||
# Unicode
|
||||
precomposed
|
||||
|
||||
# GitHub issue/PR commands
|
||||
azp
|
||||
feedbackhub
|
||||
|
||||
1
.github/actions/spell-check/expect.txt
vendored
1
.github/actions/spell-check/expect.txt
vendored
@@ -1810,7 +1810,6 @@ TILEDWINDOW
|
||||
TILLSON
|
||||
timedate
|
||||
timediff
|
||||
timeunion
|
||||
timeutil
|
||||
TITLEBARINFO
|
||||
Titlecase
|
||||
|
||||
@@ -131,6 +131,8 @@
|
||||
|
||||
"PowerToys.ImageResizer.exe",
|
||||
"PowerToys.ImageResizer.dll",
|
||||
"WinUI3Apps\\PowerToys.ImageResizerCLI.exe",
|
||||
"WinUI3Apps\\PowerToys.ImageResizerCLI.dll",
|
||||
"PowerToys.ImageResizerExt.dll",
|
||||
"PowerToys.ImageResizerContextMenu.dll",
|
||||
"ImageResizerContextMenuPackage.msix",
|
||||
|
||||
@@ -444,6 +444,10 @@ _If you want to find diagnostic data events in the source code, these two links
|
||||
<td>Microsoft.PowerToys.FancyZones_ZoneWindowKeyUp</td>
|
||||
<td>Occurs when a key is released while interacting with zones.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Microsoft.PowerToys.FancyZones_CLICommand</td>
|
||||
<td>Triggered when a FancyZones CLI command is executed, logging the command name and success status.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### FileExplorerAddOns
|
||||
@@ -690,6 +694,30 @@ _If you want to find diagnostic data events in the source code, these two links
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### Light Switch
|
||||
<table style="width:100%">
|
||||
<tr>
|
||||
<th>Event Name</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Microsoft.PowerToys.LightSwitch_EnableLightSwitch</td>
|
||||
<td>Triggered when Light Switch is enabled or disabled.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Microsoft.PowerToys.LightSwitch_ShortcutInvoked</td>
|
||||
<td>Occurs when the shortcut for Light Switch is invoked.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Microsoft.PowerToys.LightSwitch_ScheduleModeToggled</td>
|
||||
<td>Occurs when a new schedule mode is selected for Light Switch.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Microsoft.PowerToys.LightSwitch_ThemeTargetChanged</td>
|
||||
<td>Occurs when the options for targeting the system or apps is updated.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### Mouse Highlighter
|
||||
<table style="width:100%">
|
||||
<tr>
|
||||
|
||||
@@ -459,6 +459,10 @@
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/modules/imageresizer/ImageResizerCLI/ImageResizerCLI.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
</Folder>
|
||||
<Folder Name="/modules/imageresizer/Tests/">
|
||||
<Project Path="src/modules/imageresizer/tests/ImageResizer.UnitTests.csproj">
|
||||
|
||||
93
doc/devdocs/cli-conventions.md
Normal file
93
doc/devdocs/cli-conventions.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# CLI Conventions
|
||||
|
||||
This document describes the conventions for implementing command-line interfaces (CLI) in PowerToys modules.
|
||||
|
||||
## Library
|
||||
|
||||
Use the **System.CommandLine** library for CLI argument parsing. This is already defined in `Directory.Packages.props`:
|
||||
|
||||
```xml
|
||||
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
||||
```
|
||||
|
||||
Add the reference to your project:
|
||||
|
||||
```xml
|
||||
<PackageReference Include="System.CommandLine" />
|
||||
```
|
||||
|
||||
## Option Naming and Definition
|
||||
|
||||
- Use `--kebab-case` for long form (e.g., `--shrink-only`).
|
||||
- Use single `-x` for short form (e.g., `-s`, `-w`).
|
||||
- Define aliases as static readonly arrays: `["--silent", "-s"]`.
|
||||
- Create options using `Option<T>` with descriptive help text.
|
||||
- Add validators for options that require range or format checking.
|
||||
|
||||
## RootCommand Setup
|
||||
|
||||
- Create a `RootCommand` with a brief description.
|
||||
- Add all options and arguments to the command.
|
||||
|
||||
## Parsing
|
||||
|
||||
- Use `Parser(rootCommand).Parse(args)` to parse CLI arguments.
|
||||
- Extract option values using `parseResult.GetValueForOption()`.
|
||||
- Note: Use `Parser` directly; `RootCommand.Parse()` may not be available with the pinned System.CommandLine version.
|
||||
|
||||
### Parse/Validation Errors
|
||||
|
||||
- On parse/validation errors, print error messages and usage, then exit with non-zero code.
|
||||
|
||||
## Examples
|
||||
|
||||
Reference implementations:
|
||||
- Awake: `src/modules/Awake/Awake/Program.cs`
|
||||
- ImageResizer: `src/modules/imageresizer/ui/Cli/`
|
||||
|
||||
## Help Output
|
||||
|
||||
- Provide a `PrintUsage()` method for custom help formatting if needed.
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Consistency**: Follow existing module patterns.
|
||||
2. **Documentation**: Always provide help text for each option.
|
||||
3. **Validation**: Validate input and provide clear error messages.
|
||||
4. **Atomicity**: Make one logical change per PR; avoid drive-by refactors.
|
||||
5. **Build/Test Discipline**: Build and test synchronously, one terminal per operation.
|
||||
6. **Style**: Follow repo analyzers (`.editorconfig`, StyleCop) and formatting rules.
|
||||
|
||||
## Logging Requirements
|
||||
|
||||
- Use `ManagedCommon.Logger` for consistent logging.
|
||||
- Initialize logging early in `Main()`.
|
||||
- Use dual output (console + log file) for errors and warnings to ensure visibility.
|
||||
- Reference: `src/modules/imageresizer/ui/Cli/CliLogger.cs`
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Exit Codes
|
||||
|
||||
- `0`: Success
|
||||
- `1`: General error (parsing, validation, runtime)
|
||||
- `2`: Invalid arguments (optional)
|
||||
|
||||
### Exception Handling
|
||||
|
||||
- Always wrap `Main()` in try-catch for unhandled exceptions.
|
||||
- Log exceptions before exiting with non-zero code.
|
||||
- Display user-friendly error messages to stderr.
|
||||
- Preserve detailed stack traces in log files only.
|
||||
|
||||
## Testing Requirements
|
||||
|
||||
- Include tests for argument parsing, validation, and edge cases.
|
||||
- Place CLI tests in module-specific test projects (e.g., `src/modules/[module]/tests/*CliTests.cs`).
|
||||
|
||||
## Signing and Deployment
|
||||
|
||||
- CLI executables are signed automatically in CI/CD.
|
||||
- **New CLI tools**: Add your executable and dll to `.pipelines/ESRPSigning_core.json` in the signing list.
|
||||
- CLI executables are deployed alongside their parent module (e.g., `C:\Program Files\PowerToys\modules\[ModuleName]\`).
|
||||
- Use self-contained deployment (import `Common.SelfContained.props`).
|
||||
@@ -58,8 +58,8 @@ string validUIDisplayString = Resources.ValidUIDisplayString;
|
||||
## More On Coding Guidance
|
||||
Please review these brief docs below relating to our coding standards, etc.
|
||||
|
||||
* [Coding Style](./style.md)
|
||||
* [Code Organization](./readme.md)
|
||||
* [Coding Style](development/style.md)
|
||||
* [Code Organization](readme.md)
|
||||
|
||||
|
||||
[VS Resource Editor]: https://learn.microsoft.com/cpp/windows/resource-editors?view=vs-2019
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# Find My Mouse Cursor Magnifier Overlay
|
||||
|
||||
## Summary
|
||||
|
||||
Add a cursor magnifier overlay that draws a scaled copy of the current system cursor while Find My Mouse is active. This provides a "larger cursor" effect without changing the system cursor scheme or managing cursor assets.
|
||||
|
||||
## Goals
|
||||
|
||||
- Show a visibly larger cursor during the Find My Mouse spotlight effect.
|
||||
- Avoid global cursor changes and asset maintenance.
|
||||
- Keep the real system cursor visible and unmodified.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Changing the system cursor size globally.
|
||||
- Replacing cursor assets or updating cursor registry schemes.
|
||||
- Providing a configurable scale (initial implementation uses a fixed scale).
|
||||
|
||||
## Proposed Solution
|
||||
|
||||
Create a lightweight, topmost, layered overlay window that:
|
||||
|
||||
- Polls the current cursor (`GetCursorInfo`, `GetIconInfo`).
|
||||
- Renders a scaled cursor into a 32-bit DIB via `DrawIconEx`.
|
||||
- Composites the result using `UpdateLayeredWindow`.
|
||||
- Runs at ~60 Hz while the Find My Mouse effect is active.
|
||||
|
||||
This overlay is independent of the spotlight window and does not interfere with the system cursor itself.
|
||||
|
||||
## Integration Points
|
||||
|
||||
- Initialize the overlay in `FindMyMouseMain` after the sonar window is created.
|
||||
- Show/hide the overlay from `SuperSonar::StartSonar` and `SuperSonar::StopSonar`.
|
||||
- Terminate the overlay when the module is disabled.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The overlay window uses `WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE | WS_EX_TOPMOST`.
|
||||
- The scaled cursor position uses the current cursor hotspot multiplied by the scale factor.
|
||||
- If the cursor is hidden, the overlay hides itself as well.
|
||||
- The overlay allocates a DIB buffer sized to the scaled cursor and reuses it until the size changes.
|
||||
|
||||
## Risks and Considerations
|
||||
|
||||
- Performance: 60 Hz rendering can be expensive on low-end machines; consider throttling or caching if needed.
|
||||
- DPI/scale: the overlay operates in screen pixels; verify on mixed-DPI setups.
|
||||
- Z-order: topmost layered window should stay above most content, but might need adjustment if other topmost overlays are present.
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
- Expose cursor scale as a setting.
|
||||
- Cache rendered cursor bitmaps per `HCURSOR` to reduce per-frame work.
|
||||
- Consider a composition-based drawing path for smoother integration with existing visuals.
|
||||
@@ -57,7 +57,7 @@ Once you've discussed your proposed feature/fix/etc. with a team member, and an
|
||||
## Rules
|
||||
|
||||
- **Follow the pattern of what you already see in the code.**
|
||||
- [Coding style](style.md).
|
||||
- [Coding style](development/style.md).
|
||||
- Try to package new functionality/components into libraries that have nicely defined interfaces.
|
||||
- Package new functionality into classes or refactor existing functionality into a class as you extend the code.
|
||||
- When adding new classes/methods/changing existing code, add new unit tests or update the existing tests.
|
||||
|
||||
@@ -50,6 +50,8 @@ Contact the developers of a plugin directly for assistance with a specific plugi
|
||||
| [Hotkeys](https://github.com/ruslanlap/PowerToysRun-Hotkeys) | [ruslanlap](https://github.com/ruslanlap) | Create, manage, and trigger custom keyboard shortcuts directly from PowerToys Run. |
|
||||
| [RandomGen](https://github.com/ruslanlap/PowerToysRun-RandomGen) | [ruslanlap](https://github.com/ruslanlap) | 🎲 Generate random data instantly with a single keystroke. Perfect for developers, testers, designers, and anyone who needs quick access to random data. Features include secure passwords, PINs, names, business data, dates, numbers, GUIDs, color codes, and more. Especially useful for designers who need random color codes and placeholder content. |
|
||||
| [Open With Cursor](https://github.com/VictorNoxx/PowerToys-Run-Cursor/) | [VictorNoxx](https://github.com/VictorNoxx) | Open Visual Studio, VS Code recents with Cursor AI |
|
||||
| [Open With Antigravity](https://github.com/artickc/PowerToys-Run-Antygravity) | [artickc](https://github.com/artickc) | Open Visual Studio, VS Code recents with Antigravity AI |
|
||||
| [Project Launcher Plugin](https://github.com/artickc/ProjectLauncherPowerToysPlugin) | [artickc](https://github.com/artickc) | Access your projects using Project Launcher and PowerToys Run |
|
||||
| [CheatSheets](https://github.com/ruslanlap/PowerToysRun-CheatSheets) | [ruslanlap](https://github.com/ruslanlap) | 📚 Find cheat sheets and command examples instantly from tldr pages, cheat.sh, and devhints.io. Features include favorites system, categories, offline mode, and smart caching. |
|
||||
| [QuickAI](https://github.com/ruslanlap/PowerToysRun-QuickAi) | [ruslanlap](https://github.com/ruslanlap) | AI-powered assistance with instant, smart responses from multiple providers (Groq, Together, Fireworks, OpenRouter, Cohere) |
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace Microsoft.PowerToys.FilePreviewCommon
|
||||
var softlineBreak = new Markdig.Extensions.Hardlines.SoftlineBreakAsHardlineExtension();
|
||||
|
||||
MarkdownPipelineBuilder pipelineBuilder;
|
||||
pipelineBuilder = new MarkdownPipelineBuilder().UseAdvancedExtensions().UseEmojiAndSmiley().UseYamlFrontMatter().UseMathematics();
|
||||
pipelineBuilder = new MarkdownPipelineBuilder().UseAdvancedExtensions().UseEmojiAndSmiley().UseYamlFrontMatter().UseMathematics().DisableHtml();
|
||||
pipelineBuilder.Extensions.Add(extension);
|
||||
pipelineBuilder.Extensions.Add(softlineBreak);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,6 +405,7 @@ public:
|
||||
{
|
||||
m_enabled = true;
|
||||
Logger::info(L"Enabling Light Switch module...");
|
||||
Trace::Enable(true);
|
||||
|
||||
unsigned long powertoys_pid = GetCurrentProcessId();
|
||||
std::wstring args = L"--pid " + std::to_wstring(powertoys_pid);
|
||||
@@ -482,7 +483,8 @@ public:
|
||||
CloseHandle(m_process);
|
||||
m_process = nullptr;
|
||||
}
|
||||
|
||||
|
||||
Trace::Enable(false);
|
||||
StopToggleListener();
|
||||
}
|
||||
|
||||
@@ -539,6 +541,8 @@ public:
|
||||
if (m_enabled)
|
||||
{
|
||||
Logger::trace(L"Light Switch hotkey pressed");
|
||||
Trace::ShortcutInvoked();
|
||||
|
||||
if (!is_process_running())
|
||||
{
|
||||
enable();
|
||||
|
||||
@@ -19,12 +19,21 @@ void Trace::UnregisterProvider()
|
||||
TraceLoggingUnregister(g_hProvider);
|
||||
}
|
||||
|
||||
void Trace::MyEvent()
|
||||
void Trace::Enable(bool enabled) noexcept
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"PowerToyName_MyEvent",
|
||||
"LightSwitch_EnableLightSwitch",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingBoolean(enabled, "Enabled"));
|
||||
}
|
||||
|
||||
void Trace::ShortcutInvoked() noexcept
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"LightSwitch_ShortcutInvoked",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
}
|
||||
|
||||
@@ -11,5 +11,6 @@ class Trace
|
||||
public:
|
||||
static void RegisterProvider();
|
||||
static void UnregisterProvider();
|
||||
static void MyEvent();
|
||||
static void Enable(bool enabled) noexcept;
|
||||
static void ShortcutInvoked() noexcept;
|
||||
};
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "LightSwitchStateManager.h"
|
||||
#include <LightSwitchUtils.h>
|
||||
#include <NightLightRegistryObserver.h>
|
||||
#include <trace.h>
|
||||
|
||||
SERVICE_STATUS g_ServiceStatus = {};
|
||||
SERVICE_STATUS_HANDLE g_StatusHandle = nullptr;
|
||||
@@ -357,6 +358,8 @@ DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
|
||||
|
||||
int APIENTRY wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
|
||||
{
|
||||
Trace::LightSwitch::RegisterProvider();
|
||||
|
||||
if (powertoys_gpo::getConfiguredLightSwitchEnabledValue() == powertoys_gpo::gpo_rule_configured_disabled)
|
||||
{
|
||||
wchar_t msg[160];
|
||||
@@ -364,12 +367,14 @@ int APIENTRY wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
|
||||
msg,
|
||||
L"Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator.");
|
||||
Logger::info(msg);
|
||||
Trace::LightSwitch::UnregisterProvider();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int argc = 0;
|
||||
LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
||||
int rc = _tmain(argc, argv); // reuse your existing logic
|
||||
LocalFree(argv);
|
||||
|
||||
Trace::LightSwitch::UnregisterProvider();
|
||||
return rc;
|
||||
}
|
||||
@@ -80,6 +80,7 @@
|
||||
<ClCompile Include="SettingsConstants.cpp" />
|
||||
<ClCompile Include="ThemeHelper.cpp" />
|
||||
<ClCompile Include="ThemeScheduler.cpp" />
|
||||
<ClCompile Include="trace.cpp" />
|
||||
<ClCompile Include="WinHookEventIDs.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -94,6 +95,7 @@
|
||||
<ClInclude Include="SettingsObserver.h" />
|
||||
<ClInclude Include="ThemeHelper.h" />
|
||||
<ClInclude Include="ThemeScheduler.h" />
|
||||
<ClInclude Include="trace.h" />
|
||||
<ClInclude Include="WinHookEventIDs.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -39,6 +39,9 @@
|
||||
<ClCompile Include="NightLightRegistryObserver.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="trace.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ThemeScheduler.h">
|
||||
@@ -68,6 +71,9 @@
|
||||
<ClInclude Include="NightLightRegistryObserver.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="trace.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <logger.h>
|
||||
#include <LightSwitchService/trace.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -151,6 +152,7 @@ void LightSwitchSettings::LoadSettings()
|
||||
if (m_settings.scheduleMode != newMode)
|
||||
{
|
||||
m_settings.scheduleMode = newMode;
|
||||
Trace::LightSwitch::ScheduleModeToggled(val);
|
||||
NotifyObservers(SettingId::ScheduleMode);
|
||||
}
|
||||
}
|
||||
@@ -220,6 +222,8 @@ void LightSwitchSettings::LoadSettings()
|
||||
}
|
||||
}
|
||||
|
||||
bool themeTargetChanged = false;
|
||||
|
||||
// ChangeSystem
|
||||
if (const auto jsonVal = values.get_bool_value(L"changeSystem"))
|
||||
{
|
||||
@@ -227,6 +231,7 @@ void LightSwitchSettings::LoadSettings()
|
||||
if (m_settings.changeSystem != val)
|
||||
{
|
||||
m_settings.changeSystem = val;
|
||||
themeTargetChanged = true;
|
||||
NotifyObservers(SettingId::ChangeSystem);
|
||||
}
|
||||
}
|
||||
@@ -238,9 +243,16 @@ void LightSwitchSettings::LoadSettings()
|
||||
if (m_settings.changeApps != val)
|
||||
{
|
||||
m_settings.changeApps = val;
|
||||
themeTargetChanged = true;
|
||||
NotifyObservers(SettingId::ChangeApps);
|
||||
}
|
||||
}
|
||||
|
||||
// For ChangeSystem/ChangeApps changes, log telemetry
|
||||
if (themeTargetChanged)
|
||||
{
|
||||
Trace::LightSwitch::ThemeTargetChanged(m_settings.changeApps, m_settings.changeSystem);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
|
||||
43
src/modules/LightSwitch/LightSwitchService/trace.cpp
Normal file
43
src/modules/LightSwitch/LightSwitchService/trace.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
#include "pch.h"
|
||||
#include "trace.h"
|
||||
|
||||
// Telemetry strings should not be localized.
|
||||
#define LoggingProviderKey "Microsoft.PowerToys"
|
||||
|
||||
TRACELOGGING_DEFINE_PROVIDER(
|
||||
g_hProvider,
|
||||
LoggingProviderKey,
|
||||
// {38e8889b-9731-53f5-e901-e8a7c1753074}
|
||||
(0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
|
||||
TraceLoggingOptionProjectTelemetry());
|
||||
|
||||
void Trace::LightSwitch::RegisterProvider()
|
||||
{
|
||||
TraceLoggingRegister(g_hProvider);
|
||||
}
|
||||
|
||||
void Trace::LightSwitch::UnregisterProvider()
|
||||
{
|
||||
TraceLoggingUnregister(g_hProvider);
|
||||
}
|
||||
|
||||
void Trace::LightSwitch::ScheduleModeToggled(const std::wstring& newMode) noexcept
|
||||
{
|
||||
TraceLoggingWriteWrapper(
|
||||
g_hProvider,
|
||||
"LightSwitch_ScheduleModeToggled",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingWideString(newMode.c_str(), "NewMode"));
|
||||
}
|
||||
|
||||
void Trace::LightSwitch::ThemeTargetChanged(bool changeApps, bool changeSystem) noexcept
|
||||
{
|
||||
TraceLoggingWriteWrapper(
|
||||
g_hProvider,
|
||||
"LightSwitch_ThemeTargetChanged",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingBoolean(changeApps, "ChangeApps"),
|
||||
TraceLoggingBoolean(changeSystem, "ChangeSystem"));
|
||||
}
|
||||
17
src/modules/LightSwitch/LightSwitchService/trace.h
Normal file
17
src/modules/LightSwitch/LightSwitchService/trace.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/Telemetry/TraceBase.h>
|
||||
#include <string>
|
||||
|
||||
class Trace
|
||||
{
|
||||
public:
|
||||
class LightSwitch : public telemetry::TraceBase
|
||||
{
|
||||
public:
|
||||
static void RegisterProvider();
|
||||
static void UnregisterProvider();
|
||||
static void ScheduleModeToggled(const std::wstring& newMode) noexcept;
|
||||
static void ThemeTargetChanged(bool changeApps, bool changeSystem) noexcept;
|
||||
};
|
||||
};
|
||||
525
src/modules/MouseUtils/FindMyMouse/CursorMagnifierOverlay.cpp
Normal file
525
src/modules/MouseUtils/FindMyMouse/CursorMagnifierOverlay.cpp
Normal file
@@ -0,0 +1,525 @@
|
||||
#include "pch.h"
|
||||
#include "CursorMagnifierOverlay.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
// System cursor IDs (same values as OCR_* when OEMRESOURCE is defined).
|
||||
static constexpr UINT kCursorIdNormal = 32512;
|
||||
static const UINT kCursorIds[] = {
|
||||
kCursorIdNormal, // OCR_NORMAL
|
||||
32513, // OCR_IBEAM
|
||||
32514, // OCR_WAIT
|
||||
32515, // OCR_CROSS
|
||||
32516, // OCR_UP
|
||||
32642, // OCR_SIZENWSE
|
||||
32643, // OCR_SIZENESW
|
||||
32644, // OCR_SIZEWE
|
||||
32645, // OCR_SIZENS
|
||||
32646, // OCR_SIZEALL
|
||||
32648, // OCR_NO
|
||||
32649, // OCR_HAND
|
||||
32650, // OCR_APPSTARTING
|
||||
32651, // OCR_HELP
|
||||
};
|
||||
}
|
||||
|
||||
CursorMagnifierOverlay::~CursorMagnifierOverlay()
|
||||
{
|
||||
DestroyWindowInternal();
|
||||
}
|
||||
|
||||
bool CursorMagnifierOverlay::Initialize(HINSTANCE instance)
|
||||
{
|
||||
if (m_hwnd)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
m_instance = instance;
|
||||
|
||||
WNDCLASS wc{};
|
||||
if (!GetClassInfoW(instance, kWindowClassName, &wc))
|
||||
{
|
||||
wc.lpfnWndProc = WndProc;
|
||||
wc.hInstance = instance;
|
||||
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||
wc.hbrBackground = static_cast<HBRUSH>(GetStockObject(NULL_BRUSH));
|
||||
wc.lpszClassName = kWindowClassName;
|
||||
|
||||
if (!RegisterClassW(&wc))
|
||||
{
|
||||
Logger::error("RegisterClassW failed for cursor magnifier. GetLastError={}", GetLastError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DWORD exStyle = WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE | WS_EX_TOPMOST;
|
||||
m_hwnd = CreateWindowExW(
|
||||
exStyle,
|
||||
kWindowClassName,
|
||||
L"PowerToys FindMyMouse Cursor Magnifier",
|
||||
WS_POPUP,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
instance,
|
||||
this);
|
||||
|
||||
if (!m_hwnd)
|
||||
{
|
||||
Logger::error("CreateWindowExW failed for cursor magnifier. GetLastError={}", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CursorMagnifierOverlay::Terminate()
|
||||
{
|
||||
if (!m_hwnd)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PostMessage(m_hwnd, WM_CLOSE, 0, 0);
|
||||
}
|
||||
|
||||
void CursorMagnifierOverlay::SetVisible(bool visible)
|
||||
{
|
||||
if (!m_hwnd || m_visible == visible)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_visible = visible;
|
||||
if (visible)
|
||||
{
|
||||
BeginScaleAnimation();
|
||||
HideSystemCursors();
|
||||
SetTimer(m_hwnd, kTimerId, kFrameIntervalMs, nullptr);
|
||||
ShowWindow(m_hwnd, SW_SHOWNOACTIVATE);
|
||||
SetWindowPos(m_hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
|
||||
Render();
|
||||
}
|
||||
else
|
||||
{
|
||||
KillTimer(m_hwnd, kTimerId);
|
||||
ShowWindow(m_hwnd, SW_HIDE);
|
||||
ResetCursorMetrics();
|
||||
m_animationStartTick = 0;
|
||||
RestoreSystemCursors();
|
||||
}
|
||||
}
|
||||
|
||||
void CursorMagnifierOverlay::SetScale(float scale)
|
||||
{
|
||||
if (scale > 0.0f && m_targetScale != scale)
|
||||
{
|
||||
m_targetScale = scale;
|
||||
if (m_visible)
|
||||
{
|
||||
m_startScale = m_currentScale;
|
||||
m_animationStartTick = GetTickCount64();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CursorMagnifierOverlay::SetAnimationDurationMs(int durationMs)
|
||||
{
|
||||
if (durationMs > 0)
|
||||
{
|
||||
m_animationDurationMs = static_cast<DWORD>(durationMs);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_animationDurationMs = 1;
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT CALLBACK CursorMagnifierOverlay::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) noexcept
|
||||
{
|
||||
CursorMagnifierOverlay* self = nullptr;
|
||||
if (message == WM_NCCREATE)
|
||||
{
|
||||
auto create = reinterpret_cast<LPCREATESTRUCT>(lParam);
|
||||
self = static_cast<CursorMagnifierOverlay*>(create->lpCreateParams);
|
||||
SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(self));
|
||||
self->m_hwnd = hwnd;
|
||||
}
|
||||
else
|
||||
{
|
||||
self = reinterpret_cast<CursorMagnifierOverlay*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
|
||||
}
|
||||
|
||||
if (!self)
|
||||
{
|
||||
return DefWindowProc(hwnd, message, wParam, lParam);
|
||||
}
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case WM_TIMER:
|
||||
if (wParam == kTimerId)
|
||||
{
|
||||
self->OnTimer();
|
||||
}
|
||||
return 0;
|
||||
case WM_NCHITTEST:
|
||||
return HTTRANSPARENT;
|
||||
case WM_DESTROY:
|
||||
self->CleanupResources();
|
||||
return 0;
|
||||
}
|
||||
|
||||
return DefWindowProc(hwnd, message, wParam, lParam);
|
||||
}
|
||||
|
||||
void CursorMagnifierOverlay::OnTimer()
|
||||
{
|
||||
if (!m_visible)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Render();
|
||||
}
|
||||
|
||||
void CursorMagnifierOverlay::Render()
|
||||
{
|
||||
if (!m_hwnd)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CURSORINFO ci{};
|
||||
ci.cbSize = sizeof(ci);
|
||||
if (!GetCursorInfo(&ci) || (ci.flags & CURSOR_SHOWING) == 0)
|
||||
{
|
||||
ShowWindow(m_hwnd, SW_HIDE);
|
||||
return;
|
||||
}
|
||||
|
||||
HCURSOR cursorToDraw = ci.hCursor;
|
||||
if (m_systemCursorsHidden)
|
||||
{
|
||||
auto hiddenIt = m_hiddenCursorIds.find(ci.hCursor);
|
||||
if (hiddenIt != m_hiddenCursorIds.end())
|
||||
{
|
||||
auto originalIt = m_originalCursors.find(hiddenIt->second);
|
||||
if (originalIt != m_originalCursors.end() && originalIt->second)
|
||||
{
|
||||
cursorToDraw = originalIt->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UpdateCursorMetrics(cursorToDraw);
|
||||
|
||||
const float scale = GetAnimatedScale();
|
||||
int srcW = m_cursorSize.cx;
|
||||
int srcH = m_cursorSize.cy;
|
||||
if (srcW <= 0 || srcH <= 0)
|
||||
{
|
||||
srcW = GetSystemMetrics(SM_CXCURSOR);
|
||||
srcH = GetSystemMetrics(SM_CYCURSOR);
|
||||
}
|
||||
|
||||
const int dstW = (std::max)(1, static_cast<int>(std::lround(srcW * scale)));
|
||||
const int dstH = (std::max)(1, static_cast<int>(std::lround(srcH * scale)));
|
||||
|
||||
EnsureResources(dstW, dstH);
|
||||
if (!m_memDc || !m_bits)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::memset(m_bits, 0, static_cast<size_t>(dstW) * static_cast<size_t>(dstH) * 4);
|
||||
if (cursorToDraw)
|
||||
{
|
||||
DrawIconEx(m_memDc, 0, 0, cursorToDraw, dstW, dstH, 0, nullptr, DI_NORMAL);
|
||||
}
|
||||
|
||||
const int x = static_cast<int>(std::lround(ci.ptScreenPos.x - m_hotspot.x * scale));
|
||||
const int y = static_cast<int>(std::lround(ci.ptScreenPos.y - m_hotspot.y * scale));
|
||||
POINT ptDst{ x, y };
|
||||
POINT ptSrc{ 0, 0 };
|
||||
SIZE size{ dstW, dstH };
|
||||
BLENDFUNCTION blend{};
|
||||
blend.BlendOp = AC_SRC_OVER;
|
||||
blend.SourceConstantAlpha = 255;
|
||||
blend.AlphaFormat = AC_SRC_ALPHA;
|
||||
|
||||
UpdateLayeredWindow(m_hwnd, nullptr, &ptDst, &size, m_memDc, &ptSrc, 0, &blend, ULW_ALPHA);
|
||||
ShowWindow(m_hwnd, SW_SHOWNOACTIVATE);
|
||||
}
|
||||
|
||||
void CursorMagnifierOverlay::BeginScaleAnimation()
|
||||
{
|
||||
m_startScale = (m_targetScale >= 1.0f) ? 1.0f : m_targetScale;
|
||||
m_currentScale = m_startScale;
|
||||
m_animationStartTick = GetTickCount64();
|
||||
}
|
||||
|
||||
float CursorMagnifierOverlay::GetAnimatedScale()
|
||||
{
|
||||
if (m_animationDurationMs <= 1 || !m_visible)
|
||||
{
|
||||
m_currentScale = m_targetScale;
|
||||
return m_currentScale;
|
||||
}
|
||||
|
||||
if (m_animationStartTick == 0)
|
||||
{
|
||||
m_animationStartTick = GetTickCount64();
|
||||
}
|
||||
|
||||
const ULONGLONG now = GetTickCount64();
|
||||
const ULONGLONG elapsed = now - m_animationStartTick;
|
||||
if (elapsed >= m_animationDurationMs)
|
||||
{
|
||||
m_currentScale = m_targetScale;
|
||||
return m_currentScale;
|
||||
}
|
||||
|
||||
const float t = static_cast<float>(elapsed) / static_cast<float>(m_animationDurationMs);
|
||||
m_currentScale = m_startScale + (m_targetScale - m_startScale) * t;
|
||||
return m_currentScale;
|
||||
}
|
||||
|
||||
bool CursorMagnifierOverlay::HideSystemCursors()
|
||||
{
|
||||
if (m_systemCursorsHidden)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
m_hiddenCursorIds.clear();
|
||||
ReleaseOriginalCursors();
|
||||
|
||||
bool anyHidden = false;
|
||||
bool normalHidden = false;
|
||||
for (UINT cursorId : kCursorIds)
|
||||
{
|
||||
HCURSOR systemCursor = LoadCursor(nullptr, MAKEINTRESOURCE(cursorId));
|
||||
if (systemCursor)
|
||||
{
|
||||
HCURSOR copy = CopyIcon(systemCursor);
|
||||
if (copy)
|
||||
{
|
||||
m_originalCursors[cursorId] = copy;
|
||||
}
|
||||
}
|
||||
|
||||
HCURSOR transparent = CreateTransparentCursor();
|
||||
if (!transparent)
|
||||
{
|
||||
Logger::warn("CreateTransparentCursor failed for cursor id {}. GetLastError={}", cursorId, GetLastError());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!SetSystemCursor(transparent, cursorId))
|
||||
{
|
||||
Logger::warn("SetSystemCursor failed for cursor id {}. GetLastError={}", cursorId, GetLastError());
|
||||
DestroyCursor(transparent);
|
||||
continue;
|
||||
}
|
||||
|
||||
DestroyCursor(transparent);
|
||||
|
||||
HCURSOR replacedCursor = LoadCursor(nullptr, MAKEINTRESOURCE(cursorId));
|
||||
if (replacedCursor)
|
||||
{
|
||||
m_hiddenCursorIds[replacedCursor] = cursorId;
|
||||
}
|
||||
|
||||
anyHidden = true;
|
||||
if (cursorId == kCursorIdNormal)
|
||||
{
|
||||
normalHidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!anyHidden)
|
||||
{
|
||||
m_hiddenCursorIds.clear();
|
||||
ReleaseOriginalCursors();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!normalHidden)
|
||||
{
|
||||
Logger::warn("Failed to hide OCR_NORMAL; cursor may remain visible during magnifier.");
|
||||
}
|
||||
|
||||
m_systemCursorsHidden = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CursorMagnifierOverlay::RestoreSystemCursors()
|
||||
{
|
||||
if (!m_systemCursorsHidden)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SystemParametersInfoW(SPI_SETCURSORS, 0, nullptr, 0);
|
||||
m_systemCursorsHidden = false;
|
||||
m_hiddenCursorIds.clear();
|
||||
ReleaseOriginalCursors();
|
||||
}
|
||||
|
||||
HCURSOR CursorMagnifierOverlay::CreateTransparentCursor() const
|
||||
{
|
||||
const int width = GetSystemMetrics(SM_CXCURSOR);
|
||||
const int height = GetSystemMetrics(SM_CYCURSOR);
|
||||
if (width <= 0 || height <= 0)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const int bytesPerRow = (width + 7) / 8;
|
||||
const size_t maskSize = static_cast<size_t>(bytesPerRow) * static_cast<size_t>(height);
|
||||
std::vector<BYTE> andMask(maskSize, 0xFF);
|
||||
std::vector<BYTE> xorMask(maskSize, 0x00);
|
||||
|
||||
return CreateCursor(m_instance, 0, 0, width, height, andMask.data(), xorMask.data());
|
||||
}
|
||||
|
||||
void CursorMagnifierOverlay::ReleaseOriginalCursors()
|
||||
{
|
||||
for (auto& entry : m_originalCursors)
|
||||
{
|
||||
if (entry.second)
|
||||
{
|
||||
DestroyIcon(entry.second);
|
||||
}
|
||||
}
|
||||
m_originalCursors.clear();
|
||||
}
|
||||
|
||||
void CursorMagnifierOverlay::UpdateCursorMetrics(HCURSOR cursor)
|
||||
{
|
||||
if (!cursor || cursor == m_cachedCursor)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_cursorSize = { 0, 0 };
|
||||
m_hotspot = { 0, 0 };
|
||||
|
||||
ICONINFO ii{};
|
||||
if (GetIconInfo(cursor, &ii))
|
||||
{
|
||||
if (ii.hbmColor)
|
||||
{
|
||||
BITMAP bm{};
|
||||
GetObject(ii.hbmColor, sizeof(bm), &bm);
|
||||
m_cursorSize = { bm.bmWidth, bm.bmHeight };
|
||||
}
|
||||
else if (ii.hbmMask)
|
||||
{
|
||||
BITMAP bm{};
|
||||
GetObject(ii.hbmMask, sizeof(bm), &bm);
|
||||
m_cursorSize = { bm.bmWidth, bm.bmHeight / 2 };
|
||||
}
|
||||
|
||||
m_hotspot = { static_cast<LONG>(ii.xHotspot), static_cast<LONG>(ii.yHotspot) };
|
||||
|
||||
if (ii.hbmColor)
|
||||
{
|
||||
DeleteObject(ii.hbmColor);
|
||||
}
|
||||
if (ii.hbmMask)
|
||||
{
|
||||
DeleteObject(ii.hbmMask);
|
||||
}
|
||||
}
|
||||
|
||||
m_cachedCursor = cursor;
|
||||
if (m_cursorSize.cx <= 0 || m_cursorSize.cy <= 0)
|
||||
{
|
||||
m_cursorSize = { GetSystemMetrics(SM_CXCURSOR), GetSystemMetrics(SM_CYCURSOR) };
|
||||
}
|
||||
}
|
||||
|
||||
void CursorMagnifierOverlay::ResetCursorMetrics()
|
||||
{
|
||||
m_cachedCursor = nullptr;
|
||||
m_cursorSize = { 0, 0 };
|
||||
m_hotspot = { 0, 0 };
|
||||
}
|
||||
|
||||
void CursorMagnifierOverlay::EnsureResources(int width, int height)
|
||||
{
|
||||
if (width <= 0 || height <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_dib && m_dibSize.cx == width && m_dibSize.cy == height)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CleanupResources();
|
||||
|
||||
m_memDc = CreateCompatibleDC(nullptr);
|
||||
if (!m_memDc)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BITMAPINFO bmi{};
|
||||
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bmi.bmiHeader.biWidth = width;
|
||||
bmi.bmiHeader.biHeight = -height; // top-down DIB
|
||||
bmi.bmiHeader.biPlanes = 1;
|
||||
bmi.bmiHeader.biBitCount = 32;
|
||||
bmi.bmiHeader.biCompression = BI_RGB;
|
||||
|
||||
m_dib = CreateDIBSection(m_memDc, &bmi, DIB_RGB_COLORS, &m_bits, nullptr, 0);
|
||||
if (!m_dib || !m_bits)
|
||||
{
|
||||
CleanupResources();
|
||||
return;
|
||||
}
|
||||
|
||||
SelectObject(m_memDc, m_dib);
|
||||
m_dibSize = { width, height };
|
||||
}
|
||||
|
||||
void CursorMagnifierOverlay::CleanupResources()
|
||||
{
|
||||
if (m_dib)
|
||||
{
|
||||
DeleteObject(m_dib);
|
||||
m_dib = nullptr;
|
||||
}
|
||||
if (m_memDc)
|
||||
{
|
||||
DeleteDC(m_memDc);
|
||||
m_memDc = nullptr;
|
||||
}
|
||||
m_bits = nullptr;
|
||||
m_dibSize = { 0, 0 };
|
||||
}
|
||||
|
||||
void CursorMagnifierOverlay::DestroyWindowInternal()
|
||||
{
|
||||
if (m_hwnd)
|
||||
{
|
||||
DestroyWindow(m_hwnd);
|
||||
m_hwnd = nullptr;
|
||||
}
|
||||
CleanupResources();
|
||||
ResetCursorMetrics();
|
||||
RestoreSystemCursors();
|
||||
}
|
||||
60
src/modules/MouseUtils/FindMyMouse/CursorMagnifierOverlay.h
Normal file
60
src/modules/MouseUtils/FindMyMouse/CursorMagnifierOverlay.h
Normal file
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
#include "pch.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
class CursorMagnifierOverlay
|
||||
{
|
||||
public:
|
||||
CursorMagnifierOverlay() = default;
|
||||
~CursorMagnifierOverlay();
|
||||
|
||||
bool Initialize(HINSTANCE instance);
|
||||
void Terminate();
|
||||
void SetVisible(bool visible);
|
||||
void SetScale(float scale);
|
||||
void SetAnimationDurationMs(int durationMs);
|
||||
|
||||
private:
|
||||
static constexpr wchar_t kWindowClassName[] = L"FindMyMouseCursorMagnifier";
|
||||
static constexpr UINT_PTR kTimerId = 1;
|
||||
static constexpr UINT kFrameIntervalMs = 16;
|
||||
|
||||
static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) noexcept;
|
||||
|
||||
void OnTimer();
|
||||
void Render();
|
||||
void EnsureResources(int width, int height);
|
||||
void CleanupResources();
|
||||
bool HideSystemCursors();
|
||||
void RestoreSystemCursors();
|
||||
HCURSOR CreateTransparentCursor() const;
|
||||
void ReleaseOriginalCursors();
|
||||
void UpdateCursorMetrics(HCURSOR cursor);
|
||||
void ResetCursorMetrics();
|
||||
void DestroyWindowInternal();
|
||||
void BeginScaleAnimation();
|
||||
float GetAnimatedScale();
|
||||
|
||||
HWND m_hwnd = nullptr;
|
||||
HINSTANCE m_instance = nullptr;
|
||||
bool m_visible = false;
|
||||
float m_targetScale = 2.0f;
|
||||
float m_startScale = 1.0f;
|
||||
float m_currentScale = 1.0f;
|
||||
ULONGLONG m_animationStartTick = 0;
|
||||
DWORD m_animationDurationMs = 500;
|
||||
|
||||
bool m_systemCursorsHidden = false;
|
||||
std::unordered_map<HCURSOR, UINT> m_hiddenCursorIds;
|
||||
std::unordered_map<UINT, HCURSOR> m_originalCursors;
|
||||
|
||||
HCURSOR m_cachedCursor = nullptr;
|
||||
SIZE m_cursorSize{ 0, 0 };
|
||||
POINT m_hotspot{ 0, 0 };
|
||||
|
||||
HDC m_memDc = nullptr;
|
||||
HBITMAP m_dib = nullptr;
|
||||
void* m_bits = nullptr;
|
||||
SIZE m_dibSize{ 0, 0 };
|
||||
};
|
||||
@@ -2,6 +2,7 @@
|
||||
//
|
||||
#include "pch.h"
|
||||
#include "FindMyMouse.h"
|
||||
#include "CursorMagnifierOverlay.h"
|
||||
#include "WinHookEventIDs.h"
|
||||
#include "trace.h"
|
||||
#include "common/utils/game_mode.h"
|
||||
@@ -26,6 +27,11 @@ namespace winrt
|
||||
using namespace winrt::Windows::System;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
CursorMagnifierOverlay* g_cursorMagnifier = nullptr;
|
||||
}
|
||||
|
||||
namespace muxc = winrt::Microsoft::UI::Composition;
|
||||
namespace muxx = winrt::Microsoft::UI::Xaml;
|
||||
namespace muxxc = winrt::Microsoft::UI::Xaml::Controls;
|
||||
@@ -56,6 +62,7 @@ protected:
|
||||
void AfterMoveSonar() {}
|
||||
void SetSonarVisibility(bool visible) = delete;
|
||||
void UpdateMouseSnooping();
|
||||
void UpdateAnimationMethod(FindMyMouseAnimationMethod method);
|
||||
bool IsForegroundAppExcluded();
|
||||
|
||||
protected:
|
||||
@@ -72,15 +79,14 @@ protected:
|
||||
|
||||
bool m_destroyed = false;
|
||||
FindMyMouseActivationMethod m_activationMethod = FIND_MY_MOUSE_DEFAULT_ACTIVATION_METHOD;
|
||||
FindMyMouseAnimationMethod m_animationMethod = FIND_MY_MOUSE_DEFAULT_ANIMATION_METHOD;
|
||||
bool m_includeWinKey = FIND_MY_MOUSE_DEFAULT_INCLUDE_WIN_KEY;
|
||||
bool m_doNotActivateOnGameMode = FIND_MY_MOUSE_DEFAULT_DO_NOT_ACTIVATE_ON_GAME_MODE;
|
||||
int m_sonarRadius = FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_RADIUS;
|
||||
int m_sonarZoomFactor = FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_INITIAL_ZOOM;
|
||||
DWORD m_fadeDuration = FIND_MY_MOUSE_DEFAULT_ANIMATION_DURATION_MS;
|
||||
int m_finalAlphaNumerator = 100; // legacy (root now always animates to 1.0; kept for GDI fallback compatibility)
|
||||
std::vector<std::wstring> m_excludedApps;
|
||||
int m_shakeMinimumDistance = FIND_MY_MOUSE_DEFAULT_SHAKE_MINIMUM_DISTANCE;
|
||||
static constexpr int FinalAlphaDenominator = 100;
|
||||
winrt::Microsoft::UI::Dispatching::DispatcherQueueController m_dispatcherQueueController{ nullptr };
|
||||
|
||||
// Don't consider movements started past these milliseconds to detect shaking.
|
||||
@@ -155,7 +161,7 @@ private:
|
||||
void DetectShake();
|
||||
bool KeyboardInputCanActivate();
|
||||
|
||||
void StartSonar();
|
||||
void StartSonar(FindMyMouseActivationMethod activationMethod);
|
||||
void StopSonar();
|
||||
};
|
||||
|
||||
@@ -275,7 +281,7 @@ LRESULT SuperSonar<D>::BaseWndProc(UINT message, WPARAM wParam, LPARAM lParam) n
|
||||
{
|
||||
if (m_sonarStart == NoSonar)
|
||||
{
|
||||
StartSonar();
|
||||
StartSonar(FindMyMouseActivationMethod::Shortcut);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -384,7 +390,7 @@ void SuperSonar<D>::OnSonarKeyboardInput(RAWINPUT const& input)
|
||||
IsEqual(m_lastKeyPos, ptCursor))
|
||||
{
|
||||
m_sonarState = SonarState::ControlDown2;
|
||||
StartSonar();
|
||||
StartSonar(m_activationMethod);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -451,7 +457,7 @@ void SuperSonar<D>::DetectShake()
|
||||
if (diagonal > 0 && distanceTravelled / diagonal > (m_shakeFactor / 100.f))
|
||||
{
|
||||
m_movementHistory.clear();
|
||||
StartSonar();
|
||||
StartSonar(m_activationMethod);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,7 +525,7 @@ void SuperSonar<D>::OnSonarMouseInput(RAWINPUT const& input)
|
||||
}
|
||||
|
||||
template<typename D>
|
||||
void SuperSonar<D>::StartSonar()
|
||||
void SuperSonar<D>::StartSonar(FindMyMouseActivationMethod activationMethod)
|
||||
{
|
||||
// Don't activate if game mode is on.
|
||||
if (m_doNotActivateOnGameMode && detect_game_mode())
|
||||
@@ -532,14 +538,22 @@ void SuperSonar<D>::StartSonar()
|
||||
return;
|
||||
}
|
||||
|
||||
Trace::MousePointerFocused();
|
||||
Trace::MousePointerFocused(static_cast<int>(activationMethod));
|
||||
// Cover the entire virtual screen.
|
||||
// HACK: Draw with 1 pixel off. Otherwise, Windows glitches the task bar transparency when a transparent window fill the whole screen.
|
||||
SetWindowPos(m_hwnd, HWND_TOPMOST, GetSystemMetrics(SM_XVIRTUALSCREEN) + 1, GetSystemMetrics(SM_YVIRTUALSCREEN) + 1, GetSystemMetrics(SM_CXVIRTUALSCREEN) - 2, GetSystemMetrics(SM_CYVIRTUALSCREEN) - 2, 0);
|
||||
m_sonarPos = ptNowhere;
|
||||
OnMouseTimer();
|
||||
UpdateMouseSnooping();
|
||||
Shim()->SetSonarVisibility(true);
|
||||
const bool showSpotlight = m_animationMethod == FindMyMouseAnimationMethod::Spotlight;
|
||||
if (showSpotlight)
|
||||
{
|
||||
Shim()->SetSonarVisibility(true);
|
||||
}
|
||||
if (g_cursorMagnifier)
|
||||
{
|
||||
g_cursorMagnifier->SetVisible(m_animationMethod == FindMyMouseAnimationMethod::CursorMagnifier);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename D>
|
||||
@@ -551,6 +565,10 @@ void SuperSonar<D>::StopSonar()
|
||||
Shim()->SetSonarVisibility(false);
|
||||
KillTimer(m_hwnd, TIMER_ID_TRACK);
|
||||
}
|
||||
if (g_cursorMagnifier)
|
||||
{
|
||||
g_cursorMagnifier->SetVisible(false);
|
||||
}
|
||||
m_sonarState = SonarState::Idle;
|
||||
UpdateMouseSnooping();
|
||||
}
|
||||
@@ -619,6 +637,27 @@ void SuperSonar<D>::UpdateMouseSnooping()
|
||||
}
|
||||
}
|
||||
|
||||
template<typename D>
|
||||
void SuperSonar<D>::UpdateAnimationMethod(FindMyMouseAnimationMethod method)
|
||||
{
|
||||
if (m_animationMethod == method)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_animationMethod = method;
|
||||
if (m_sonarStart == NoSonar)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Shim()->SetSonarVisibility(m_animationMethod == FindMyMouseAnimationMethod::Spotlight);
|
||||
if (g_cursorMagnifier)
|
||||
{
|
||||
g_cursorMagnifier->SetVisible(m_animationMethod == FindMyMouseAnimationMethod::CursorMagnifier);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename D>
|
||||
bool SuperSonar<D>::IsForegroundAppExcluded()
|
||||
{
|
||||
@@ -816,13 +855,16 @@ private:
|
||||
// Dim color (source)
|
||||
m_dimColorBrush = m_compositor.CreateColorBrush(m_backgroundColor);
|
||||
// Radial gradient mask (center transparent, outer opaque)
|
||||
// Fixed feather width: 1px for radius < 300, 2px for radius >= 300
|
||||
const float featherPixels = (m_sonarRadius >= 300) ? 2.0f : 1.0f;
|
||||
const float featherOffset = 1.0f - featherPixels / (rDip * zoom);
|
||||
m_spotlightMaskGradient = m_compositor.CreateRadialGradientBrush();
|
||||
m_spotlightMaskGradient.MappingMode(muxc::CompositionMappingMode::Absolute);
|
||||
m_maskStopCenter = m_compositor.CreateColorGradientStop();
|
||||
m_maskStopCenter.Offset(0.0f);
|
||||
m_maskStopCenter.Color(winrt::Windows::UI::ColorHelper::FromArgb(0, 0, 0, 0));
|
||||
m_maskStopInner = m_compositor.CreateColorGradientStop();
|
||||
m_maskStopInner.Offset(0.995f);
|
||||
m_maskStopInner.Offset(featherOffset);
|
||||
m_maskStopInner.Color(winrt::Windows::UI::ColorHelper::FromArgb(0, 0, 0, 0));
|
||||
m_maskStopOuter = m_compositor.CreateColorGradientStop();
|
||||
m_maskStopOuter.Offset(1.0f);
|
||||
@@ -852,23 +894,7 @@ private:
|
||||
m_root.ImplicitAnimations(collection);
|
||||
|
||||
// 6) Spotlight radius shrinks as opacity increases (expression animation)
|
||||
auto radiusExpression = m_compositor.CreateExpressionAnimation();
|
||||
radiusExpression.SetReferenceParameter(L"Root", m_root);
|
||||
|
||||
wchar_t expressionText[256];
|
||||
winrt::check_hresult(StringCchPrintfW(
|
||||
expressionText, ARRAYSIZE(expressionText), L"Lerp(Vector2(%d, %d), Vector2(%d, %d), Root.Opacity)", m_sonarRadius * m_sonarZoomFactor, m_sonarRadius * m_sonarZoomFactor, m_sonarRadius, m_sonarRadius));
|
||||
|
||||
radiusExpression.Expression(expressionText);
|
||||
m_spotlightMaskGradient.StartAnimation(L"EllipseRadius", radiusExpression);
|
||||
// Also animate spotlight geometry radius for visual consistency
|
||||
if (m_circleGeometry)
|
||||
{
|
||||
auto radiusExpression2 = m_compositor.CreateExpressionAnimation();
|
||||
radiusExpression2.SetReferenceParameter(L"Root", m_root);
|
||||
radiusExpression2.Expression(expressionText);
|
||||
m_circleGeometry.StartAnimation(L"Radius", radiusExpression2);
|
||||
}
|
||||
SetupRadiusAnimations(rDip * zoom, rDip, featherPixels);
|
||||
|
||||
// Composition created successfully
|
||||
return true;
|
||||
@@ -887,6 +913,41 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to setup radius and feather expression animations
|
||||
void SetupRadiusAnimations(float startRadiusDip, float endRadiusDip, float featherPixels)
|
||||
{
|
||||
// Radius expression: shrinks from startRadiusDip to endRadiusDip as opacity goes 0->1
|
||||
auto radiusExpression = m_compositor.CreateExpressionAnimation();
|
||||
radiusExpression.SetReferenceParameter(L"Root", m_root);
|
||||
wchar_t expressionText[256];
|
||||
winrt::check_hresult(StringCchPrintfW(
|
||||
expressionText, ARRAYSIZE(expressionText),
|
||||
L"Lerp(Vector2(%.1f, %.1f), Vector2(%.1f, %.1f), Root.Opacity)",
|
||||
startRadiusDip, startRadiusDip, endRadiusDip, endRadiusDip));
|
||||
radiusExpression.Expression(expressionText);
|
||||
m_spotlightMaskGradient.StartAnimation(L"EllipseRadius", radiusExpression);
|
||||
|
||||
// Feather expression: maintains fixed pixel width as radius changes
|
||||
auto featherExpression = m_compositor.CreateExpressionAnimation();
|
||||
featherExpression.SetReferenceParameter(L"Root", m_root);
|
||||
wchar_t featherExpressionText[256];
|
||||
winrt::check_hresult(StringCchPrintfW(
|
||||
featherExpressionText, ARRAYSIZE(featherExpressionText),
|
||||
L"1.0f - %.1ff / Lerp(%.1ff, %.1ff, Root.Opacity)",
|
||||
featherPixels, startRadiusDip, endRadiusDip));
|
||||
featherExpression.Expression(featherExpressionText);
|
||||
m_maskStopInner.StartAnimation(L"Offset", featherExpression);
|
||||
|
||||
// Circle geometry radius for visual consistency
|
||||
if (m_circleGeometry)
|
||||
{
|
||||
auto radiusExpression2 = m_compositor.CreateExpressionAnimation();
|
||||
radiusExpression2.SetReferenceParameter(L"Root", m_root);
|
||||
radiusExpression2.Expression(expressionText);
|
||||
m_circleGeometry.StartAnimation(L"Radius", radiusExpression2);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateIslandSize()
|
||||
{
|
||||
if (!m_island)
|
||||
@@ -913,6 +974,7 @@ public:
|
||||
m_backgroundColor = settings.backgroundColor;
|
||||
m_spotlightColor = settings.spotlightColor;
|
||||
m_activationMethod = settings.activationMethod;
|
||||
m_animationMethod = settings.animationMethod;
|
||||
m_includeWinKey = settings.includeWinKey;
|
||||
m_doNotActivateOnGameMode = settings.doNotActivateOnGameMode;
|
||||
m_fadeDuration = settings.animationDurationMs > 0 ? settings.animationDurationMs : 1;
|
||||
@@ -921,6 +983,10 @@ public:
|
||||
m_shakeMinimumDistance = settings.shakeMinimumDistance;
|
||||
m_shakeIntervalMs = settings.shakeIntervalMs;
|
||||
m_shakeFactor = settings.shakeFactor;
|
||||
if (g_cursorMagnifier)
|
||||
{
|
||||
g_cursorMagnifier->SetAnimationDurationMs(settings.animationDurationMs);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -939,6 +1005,7 @@ public:
|
||||
m_backgroundColor = localSettings.backgroundColor;
|
||||
m_spotlightColor = localSettings.spotlightColor;
|
||||
m_activationMethod = localSettings.activationMethod;
|
||||
UpdateAnimationMethod(localSettings.animationMethod);
|
||||
m_includeWinKey = localSettings.includeWinKey;
|
||||
m_doNotActivateOnGameMode = localSettings.doNotActivateOnGameMode;
|
||||
m_fadeDuration = localSettings.animationDurationMs > 0 ? localSettings.animationDurationMs : 1;
|
||||
@@ -948,6 +1015,10 @@ public:
|
||||
m_shakeIntervalMs = localSettings.shakeIntervalMs;
|
||||
m_shakeFactor = localSettings.shakeFactor;
|
||||
UpdateMouseSnooping(); // For the shake mouse activation method
|
||||
if (g_cursorMagnifier)
|
||||
{
|
||||
g_cursorMagnifier->SetAnimationDurationMs(localSettings.animationDurationMs);
|
||||
}
|
||||
|
||||
// Apply new settings to runtime composition objects.
|
||||
if (m_dimColorBrush)
|
||||
@@ -964,27 +1035,21 @@ public:
|
||||
const float scale = static_cast<float>(m_surface.XamlRoot().RasterizationScale());
|
||||
const float rDip = m_sonarRadiusFloat / scale;
|
||||
const float zoom = static_cast<float>(m_sonarZoomFactor);
|
||||
const float featherPixels = (m_sonarRadius >= 300) ? 2.0f : 1.0f;
|
||||
const float startRadiusDip = rDip * zoom;
|
||||
m_spotlightMaskGradient.StopAnimation(L"EllipseRadius");
|
||||
m_spotlightMaskGradient.EllipseCenter({ rDip * zoom, rDip * zoom });
|
||||
if (m_spotlight)
|
||||
{
|
||||
m_spotlight.Size({ rDip * 2 * zoom, rDip * 2 * zoom });
|
||||
m_circleShape.Offset({ rDip * zoom, rDip * zoom });
|
||||
}
|
||||
auto radiusExpression = m_compositor.CreateExpressionAnimation();
|
||||
radiusExpression.SetReferenceParameter(L"Root", m_root);
|
||||
wchar_t expressionText[256];
|
||||
winrt::check_hresult(StringCchPrintfW(expressionText, ARRAYSIZE(expressionText), L"Lerp(Vector2(%d, %d), Vector2(%d, %d), Root.Opacity)", m_sonarRadius * m_sonarZoomFactor, m_sonarRadius * m_sonarZoomFactor, m_sonarRadius, m_sonarRadius));
|
||||
radiusExpression.Expression(expressionText);
|
||||
m_spotlightMaskGradient.StartAnimation(L"EllipseRadius", radiusExpression);
|
||||
m_maskStopInner.StopAnimation(L"Offset");
|
||||
if (m_circleGeometry)
|
||||
{
|
||||
m_circleGeometry.StopAnimation(L"Radius");
|
||||
auto radiusExpression2 = m_compositor.CreateExpressionAnimation();
|
||||
radiusExpression2.SetReferenceParameter(L"Root", m_root);
|
||||
radiusExpression2.Expression(expressionText);
|
||||
m_circleGeometry.StartAnimation(L"Radius", radiusExpression2);
|
||||
}
|
||||
m_spotlightMaskGradient.EllipseCenter({ startRadiusDip, startRadiusDip });
|
||||
if (m_spotlight)
|
||||
{
|
||||
m_spotlight.Size({ rDip * 2 * zoom, rDip * 2 * zoom });
|
||||
m_circleShape.Offset({ startRadiusDip, startRadiusDip });
|
||||
}
|
||||
SetupRadiusAnimations(startRadiusDip, rDip, featherPixels);
|
||||
}
|
||||
});
|
||||
if (!enqueueSucceeded)
|
||||
@@ -1018,202 +1083,6 @@ private:
|
||||
muxc::ScalarKeyFrameAnimation m_animation{ nullptr };
|
||||
};
|
||||
|
||||
template<typename D>
|
||||
struct GdiSonar : SuperSonar<D>
|
||||
{
|
||||
LRESULT WndProc(UINT message, WPARAM wParam, LPARAM lParam) noexcept
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case WM_CREATE:
|
||||
SetLayeredWindowAttributes(this->m_hwnd, 0, 0, LWA_ALPHA);
|
||||
break;
|
||||
|
||||
case WM_TIMER:
|
||||
switch (wParam)
|
||||
{
|
||||
case TIMER_ID_FADE:
|
||||
OnFadeTimer();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_PAINT:
|
||||
this->Shim()->OnPaint();
|
||||
break;
|
||||
}
|
||||
return this->BaseWndProc(message, wParam, lParam);
|
||||
}
|
||||
|
||||
void BeforeMoveSonar() { this->Shim()->InvalidateSonar(); }
|
||||
void AfterMoveSonar() { this->Shim()->InvalidateSonar(); }
|
||||
|
||||
void SetSonarVisibility(bool visible)
|
||||
{
|
||||
m_alphaTarget = visible ? MaxAlpha : 0;
|
||||
m_fadeStart = GetTickCount() - FadeFramePeriod;
|
||||
SetTimer(this->m_hwnd, TIMER_ID_FADE, FadeFramePeriod, nullptr);
|
||||
OnFadeTimer();
|
||||
}
|
||||
|
||||
void OnFadeTimer()
|
||||
{
|
||||
auto now = GetTickCount();
|
||||
auto step = (int)((now - m_fadeStart) * MaxAlpha / this->m_fadeDuration);
|
||||
|
||||
this->Shim()->InvalidateSonar();
|
||||
if (m_alpha < m_alphaTarget)
|
||||
{
|
||||
m_alpha += step;
|
||||
if (m_alpha > m_alphaTarget)
|
||||
m_alpha = m_alphaTarget;
|
||||
}
|
||||
else if (m_alpha > m_alphaTarget)
|
||||
{
|
||||
m_alpha -= step;
|
||||
if (m_alpha < m_alphaTarget)
|
||||
m_alpha = m_alphaTarget;
|
||||
}
|
||||
SetLayeredWindowAttributes(this->m_hwnd, 0, (BYTE)m_alpha, LWA_ALPHA);
|
||||
this->Shim()->InvalidateSonar();
|
||||
if (m_alpha == m_alphaTarget)
|
||||
{
|
||||
KillTimer(this->m_hwnd, TIMER_ID_FADE);
|
||||
if (m_alpha == 0)
|
||||
{
|
||||
ShowWindow(this->m_hwnd, SW_HIDE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowWindow(this->m_hwnd, SW_SHOWNOACTIVATE);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
int CurrentSonarRadius()
|
||||
{
|
||||
int range = MaxAlpha - m_alpha;
|
||||
int radius = this->m_sonarRadius + this->m_sonarRadius * range * (this->m_sonarZoomFactor - 1) / MaxAlpha;
|
||||
return radius;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr DWORD FadeFramePeriod = 10;
|
||||
int MaxAlpha = SuperSonar<D>::m_finalAlphaNumerator * 255 / SuperSonar<D>::FinalAlphaDenominator;
|
||||
static constexpr DWORD TIMER_ID_FADE = 101;
|
||||
|
||||
private:
|
||||
int m_alpha = 0;
|
||||
int m_alphaTarget = 0;
|
||||
DWORD m_fadeStart = 0;
|
||||
};
|
||||
|
||||
struct GdiSpotlight : GdiSonar<GdiSpotlight>
|
||||
{
|
||||
void InvalidateSonar()
|
||||
{
|
||||
RECT rc;
|
||||
auto radius = CurrentSonarRadius();
|
||||
rc.left = this->m_sonarPos.x - radius;
|
||||
rc.top = this->m_sonarPos.y - radius;
|
||||
rc.right = this->m_sonarPos.x + radius;
|
||||
rc.bottom = this->m_sonarPos.y + radius;
|
||||
InvalidateRect(this->m_hwnd, &rc, FALSE);
|
||||
}
|
||||
|
||||
void OnPaint()
|
||||
{
|
||||
PAINTSTRUCT ps;
|
||||
BeginPaint(this->m_hwnd, &ps);
|
||||
|
||||
auto radius = CurrentSonarRadius();
|
||||
auto spotlight = CreateRoundRectRgn(
|
||||
this->m_sonarPos.x - radius, this->m_sonarPos.y - radius, this->m_sonarPos.x + radius, this->m_sonarPos.y + radius, radius * 2, radius * 2);
|
||||
|
||||
FillRgn(ps.hdc, spotlight, static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH)));
|
||||
Sleep(1000 / 60);
|
||||
ExtSelectClipRgn(ps.hdc, spotlight, RGN_DIFF);
|
||||
FillRect(ps.hdc, &ps.rcPaint, static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)));
|
||||
DeleteObject(spotlight);
|
||||
|
||||
EndPaint(this->m_hwnd, &ps);
|
||||
}
|
||||
};
|
||||
|
||||
struct GdiCrosshairs : GdiSonar<GdiCrosshairs>
|
||||
{
|
||||
void InvalidateSonar()
|
||||
{
|
||||
RECT rc;
|
||||
auto radius = CurrentSonarRadius();
|
||||
GetClientRect(m_hwnd, &rc);
|
||||
rc.left = m_sonarPos.x - radius;
|
||||
rc.right = m_sonarPos.x + radius;
|
||||
InvalidateRect(m_hwnd, &rc, FALSE);
|
||||
|
||||
GetClientRect(m_hwnd, &rc);
|
||||
rc.top = m_sonarPos.y - radius;
|
||||
rc.bottom = m_sonarPos.y + radius;
|
||||
InvalidateRect(m_hwnd, &rc, FALSE);
|
||||
}
|
||||
|
||||
void OnPaint()
|
||||
{
|
||||
PAINTSTRUCT ps;
|
||||
BeginPaint(this->m_hwnd, &ps);
|
||||
|
||||
auto radius = CurrentSonarRadius();
|
||||
RECT rc;
|
||||
|
||||
HBRUSH white = static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH));
|
||||
|
||||
rc.left = m_sonarPos.x - radius;
|
||||
rc.top = ps.rcPaint.top;
|
||||
rc.right = m_sonarPos.x + radius;
|
||||
rc.bottom = ps.rcPaint.bottom;
|
||||
FillRect(ps.hdc, &rc, white);
|
||||
|
||||
rc.left = ps.rcPaint.left;
|
||||
rc.top = m_sonarPos.y - radius;
|
||||
rc.right = ps.rcPaint.right;
|
||||
rc.bottom = m_sonarPos.y + radius;
|
||||
FillRect(ps.hdc, &rc, white);
|
||||
|
||||
HBRUSH black = static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
|
||||
|
||||
// Top left
|
||||
rc.left = ps.rcPaint.left;
|
||||
rc.top = ps.rcPaint.top;
|
||||
rc.right = m_sonarPos.x - radius;
|
||||
rc.bottom = m_sonarPos.y - radius;
|
||||
FillRect(ps.hdc, &rc, black);
|
||||
|
||||
// Top right
|
||||
rc.left = m_sonarPos.x + radius;
|
||||
rc.top = ps.rcPaint.top;
|
||||
rc.right = ps.rcPaint.right;
|
||||
rc.bottom = m_sonarPos.y - radius;
|
||||
FillRect(ps.hdc, &rc, black);
|
||||
|
||||
// Bottom left
|
||||
rc.left = ps.rcPaint.left;
|
||||
rc.top = m_sonarPos.y + radius;
|
||||
rc.right = m_sonarPos.x - radius;
|
||||
rc.bottom = ps.rcPaint.bottom;
|
||||
FillRect(ps.hdc, &rc, black);
|
||||
|
||||
// Bottom right
|
||||
rc.left = m_sonarPos.x + radius;
|
||||
rc.top = m_sonarPos.y + radius;
|
||||
rc.right = ps.rcPaint.right;
|
||||
rc.bottom = ps.rcPaint.bottom;
|
||||
FillRect(ps.hdc, &rc, black);
|
||||
|
||||
EndPaint(this->m_hwnd, &ps);
|
||||
}
|
||||
};
|
||||
|
||||
#pragma endregion Super_Sonar_Base_Code
|
||||
|
||||
#pragma region Super_Sonar_API
|
||||
@@ -1229,6 +1098,10 @@ void FindMyMouseApplySettings(const FindMyMouseSettings& settings)
|
||||
|
||||
void FindMyMouseDisable()
|
||||
{
|
||||
if (g_cursorMagnifier)
|
||||
{
|
||||
g_cursorMagnifier->Terminate();
|
||||
}
|
||||
if (m_sonar != nullptr)
|
||||
{
|
||||
m_sonar->Terminate();
|
||||
@@ -1258,6 +1131,18 @@ int FindMyMouseMain(HINSTANCE hinst, const FindMyMouseSettings& settings)
|
||||
}
|
||||
m_sonar = &sonar;
|
||||
|
||||
CursorMagnifierOverlay cursorMagnifier;
|
||||
g_cursorMagnifier = &cursorMagnifier;
|
||||
if (!cursorMagnifier.Initialize(hinst))
|
||||
{
|
||||
Logger::warn("Couldn't initialize cursor magnifier overlay.");
|
||||
g_cursorMagnifier = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
cursorMagnifier.SetAnimationDurationMs(settings.animationDurationMs);
|
||||
}
|
||||
|
||||
InitializeWinhookEventIds();
|
||||
|
||||
MSG msg;
|
||||
@@ -1270,6 +1155,7 @@ int FindMyMouseMain(HINSTANCE hinst, const FindMyMouseSettings& settings)
|
||||
}
|
||||
|
||||
m_sonar = nullptr;
|
||||
g_cursorMagnifier = nullptr;
|
||||
|
||||
return (int)msg.wParam;
|
||||
}
|
||||
|
||||
@@ -10,6 +10,13 @@ enum struct FindMyMouseActivationMethod : int
|
||||
EnumElements = 4, // number of elements in the enum, not counting this
|
||||
};
|
||||
|
||||
enum struct FindMyMouseAnimationMethod : int
|
||||
{
|
||||
Spotlight = 0,
|
||||
CursorMagnifier = 1,
|
||||
EnumElements = 2, // number of elements in the enum, not counting this
|
||||
};
|
||||
|
||||
constexpr bool FIND_MY_MOUSE_DEFAULT_DO_NOT_ACTIVATE_ON_GAME_MODE = true;
|
||||
// Default colors now include full alpha. Opacity is encoded directly in color alpha (legacy overlay_opacity migrated into A channel)
|
||||
const winrt::Windows::UI::Color FIND_MY_MOUSE_DEFAULT_BACKGROUND_COLOR = winrt::Windows::UI::ColorHelper::FromArgb(128, 0, 0, 0);
|
||||
@@ -18,6 +25,7 @@ constexpr int FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_RADIUS = 100;
|
||||
constexpr int FIND_MY_MOUSE_DEFAULT_ANIMATION_DURATION_MS = 500;
|
||||
constexpr int FIND_MY_MOUSE_DEFAULT_SPOTLIGHT_INITIAL_ZOOM = 9;
|
||||
constexpr FindMyMouseActivationMethod FIND_MY_MOUSE_DEFAULT_ACTIVATION_METHOD = FindMyMouseActivationMethod::DoubleLeftControlKey;
|
||||
constexpr FindMyMouseAnimationMethod FIND_MY_MOUSE_DEFAULT_ANIMATION_METHOD = FindMyMouseAnimationMethod::Spotlight;
|
||||
constexpr bool FIND_MY_MOUSE_DEFAULT_INCLUDE_WIN_KEY = false;
|
||||
constexpr int FIND_MY_MOUSE_DEFAULT_SHAKE_MINIMUM_DISTANCE = 1000;
|
||||
constexpr int FIND_MY_MOUSE_DEFAULT_SHAKE_INTERVAL_MS = 1000;
|
||||
@@ -26,6 +34,7 @@ constexpr int FIND_MY_MOUSE_DEFAULT_SHAKE_FACTOR = 400; // 400 percent
|
||||
struct FindMyMouseSettings
|
||||
{
|
||||
FindMyMouseActivationMethod activationMethod = FIND_MY_MOUSE_DEFAULT_ACTIVATION_METHOD;
|
||||
FindMyMouseAnimationMethod animationMethod = FIND_MY_MOUSE_DEFAULT_ANIMATION_METHOD;
|
||||
bool includeWinKey = FIND_MY_MOUSE_DEFAULT_INCLUDE_WIN_KEY;
|
||||
bool doNotActivateOnGameMode = FIND_MY_MOUSE_DEFAULT_DO_NOT_ACTIVATE_ON_GAME_MODE;
|
||||
winrt::Windows::UI::Color backgroundColor = FIND_MY_MOUSE_DEFAULT_BACKGROUND_COLOR;
|
||||
|
||||
@@ -106,6 +106,7 @@
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="CursorMagnifierOverlay.h" />
|
||||
<ClInclude Include="FindMyMouse.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
@@ -113,6 +114,7 @@
|
||||
<ClInclude Include="WinHookEventIDs.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="CursorMagnifierOverlay.cpp" />
|
||||
<ClCompile Include="dllmain.cpp" />
|
||||
<ClCompile Include="FindMyMouse.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
@@ -154,4 +156,4 @@
|
||||
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -33,6 +33,9 @@
|
||||
<ClCompile Include="WinHookEventIDs.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CursorMagnifierOverlay.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h">
|
||||
@@ -50,6 +53,9 @@
|
||||
<ClInclude Include="WinHookEventIDs.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CursorMagnifierOverlay.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="FindMyMouse.rc">
|
||||
@@ -59,4 +65,4 @@
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace
|
||||
const wchar_t JSON_KEY_PROPERTIES[] = L"properties";
|
||||
const wchar_t JSON_KEY_VALUE[] = L"value";
|
||||
const wchar_t JSON_KEY_ACTIVATION_METHOD[] = L"activation_method";
|
||||
const wchar_t JSON_KEY_ANIMATION_METHOD[] = L"animation_method";
|
||||
const wchar_t JSON_KEY_INCLUDE_WIN_KEY[] = L"include_win_key";
|
||||
const wchar_t JSON_KEY_DO_NOT_ACTIVATE_ON_GAME_MODE[] = L"do_not_activate_on_game_mode";
|
||||
const wchar_t JSON_KEY_BACKGROUND_COLOR[] = L"background_color";
|
||||
@@ -267,6 +268,24 @@ void FindMyMouse::parse_settings(PowerToysSettings::PowerToyValues& settings)
|
||||
Logger::warn("Failed to initialize Activation Method from settings. Will use default value");
|
||||
}
|
||||
try
|
||||
{
|
||||
// Parse Animation Method
|
||||
auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_ANIMATION_METHOD);
|
||||
int value = static_cast<int>(jsonPropertiesObject.GetNamedNumber(JSON_KEY_VALUE));
|
||||
if (value < static_cast<int>(FindMyMouseAnimationMethod::EnumElements) && value >= 0)
|
||||
{
|
||||
findMyMouseSettings.animationMethod = static_cast<FindMyMouseAnimationMethod>(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Invalid Animation Method value");
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Logger::warn("Failed to initialize Animation Method from settings. Will use default value");
|
||||
}
|
||||
try
|
||||
{
|
||||
auto jsonPropertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_INCLUDE_WIN_KEY);
|
||||
findMyMouseSettings.includeWinKey = jsonPropertiesObject.GetNamedBoolean(JSON_KEY_VALUE);
|
||||
|
||||
@@ -22,11 +22,12 @@ void Trace::EnableFindMyMouse(const bool enabled) noexcept
|
||||
}
|
||||
|
||||
// Log that the user activated the module by focusing the mouse pointer
|
||||
void Trace::MousePointerFocused() noexcept
|
||||
void Trace::MousePointerFocused(const int activationMethod) noexcept
|
||||
{
|
||||
TraceLoggingWriteWrapper(
|
||||
g_hProvider,
|
||||
"FindMyMouse_MousePointerFocused",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingInt32(activationMethod, "ActivationMethod"));
|
||||
}
|
||||
|
||||
@@ -9,5 +9,6 @@ public:
|
||||
static void EnableFindMyMouse(const bool enabled) noexcept;
|
||||
|
||||
// Log that the user activated the module by focusing the mouse pointer
|
||||
static void MousePointerFocused() noexcept;
|
||||
// activationMethod: 0 = DoubleLeftControlKey, 1 = DoubleRightControlKey, 2 = ShakeMouse, 3 = Shortcut
|
||||
static void MousePointerFocused(const int activationMethod) noexcept;
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,12 +18,12 @@ using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
using MouseWithoutBorders.Core;
|
||||
using Newtonsoft.Json;
|
||||
using StreamJsonRpc;
|
||||
|
||||
#if !MM_HELPER
|
||||
using MouseWithoutBorders.Class;
|
||||
using MouseWithoutBorders.Core;
|
||||
#endif
|
||||
|
||||
using SystemClipboard = System.Windows.Forms.Clipboard;
|
||||
@@ -246,11 +246,11 @@ WellKnownSidType.AuthenticatedUserSid, null);
|
||||
CancellationToken cancellationToken = _serverTaskCancellationSource.Token;
|
||||
|
||||
IpcChannel<ClipboardHelper>.StartIpcServer(ChannelName + "/" + RemoteObjectName, cancellationToken);
|
||||
Common.IpcChannelCreated = true;
|
||||
IpcChannelHelper.IpcChannelCreated = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Common.IpcChannelCreated = false;
|
||||
IpcChannelHelper.IpcChannelCreated = false;
|
||||
Common.ShowToolTip("Error setting up clipboard sharing, clipboard sharing will not work!", 5000, ToolTipIcon.Error);
|
||||
Logger.Log(e);
|
||||
}
|
||||
@@ -405,7 +405,7 @@ WellKnownSidType.AuthenticatedUserSid, null);
|
||||
|
||||
try
|
||||
{
|
||||
rv = Common.Retry(nameof(SystemClipboard.ContainsFileDropList), () => { return SystemClipboard.ContainsFileDropList(); }, (log) => Log(log));
|
||||
rv = IpcChannelHelper.Retry(nameof(SystemClipboard.ContainsFileDropList), () => { return SystemClipboard.ContainsFileDropList(); }, (log) => Log(log));
|
||||
}
|
||||
catch (ExternalException e)
|
||||
{
|
||||
@@ -427,7 +427,7 @@ WellKnownSidType.AuthenticatedUserSid, null);
|
||||
|
||||
try
|
||||
{
|
||||
rv = Common.Retry(nameof(SystemClipboard.ContainsImage), () => { return SystemClipboard.ContainsImage(); }, (log) => Log(log));
|
||||
rv = IpcChannelHelper.Retry(nameof(SystemClipboard.ContainsImage), () => { return SystemClipboard.ContainsImage(); }, (log) => Log(log));
|
||||
}
|
||||
catch (ExternalException e)
|
||||
{
|
||||
@@ -449,7 +449,7 @@ WellKnownSidType.AuthenticatedUserSid, null);
|
||||
|
||||
try
|
||||
{
|
||||
rv = Common.Retry(nameof(SystemClipboard.ContainsText), () => { return SystemClipboard.ContainsText(); }, (log) => Log(log));
|
||||
rv = IpcChannelHelper.Retry(nameof(SystemClipboard.ContainsText), () => { return SystemClipboard.ContainsText(); }, (log) => Log(log));
|
||||
}
|
||||
catch (ExternalException e)
|
||||
{
|
||||
@@ -471,7 +471,7 @@ WellKnownSidType.AuthenticatedUserSid, null);
|
||||
|
||||
try
|
||||
{
|
||||
rv = Common.Retry(nameof(SystemClipboard.GetFileDropList), () => { return SystemClipboard.GetFileDropList(); }, (log) => Log(log));
|
||||
rv = IpcChannelHelper.Retry(nameof(SystemClipboard.GetFileDropList), () => { return SystemClipboard.GetFileDropList(); }, (log) => Log(log));
|
||||
}
|
||||
catch (ExternalException e)
|
||||
{
|
||||
@@ -493,7 +493,7 @@ WellKnownSidType.AuthenticatedUserSid, null);
|
||||
|
||||
try
|
||||
{
|
||||
rv = Common.Retry(nameof(SystemClipboard.GetImage), () => { return SystemClipboard.GetImage(); }, (log) => Log(log));
|
||||
rv = IpcChannelHelper.Retry(nameof(SystemClipboard.GetImage), () => { return SystemClipboard.GetImage(); }, (log) => Log(log));
|
||||
}
|
||||
catch (ExternalException e)
|
||||
{
|
||||
@@ -515,7 +515,7 @@ WellKnownSidType.AuthenticatedUserSid, null);
|
||||
|
||||
try
|
||||
{
|
||||
rv = Common.Retry(nameof(SystemClipboard.GetText), () => { return SystemClipboard.GetText(format); }, (log) => Log(log));
|
||||
rv = IpcChannelHelper.Retry(nameof(SystemClipboard.GetText), () => { return SystemClipboard.GetText(format); }, (log) => Log(log));
|
||||
}
|
||||
catch (ExternalException e)
|
||||
{
|
||||
@@ -539,7 +539,7 @@ WellKnownSidType.AuthenticatedUserSid, null);
|
||||
{
|
||||
try
|
||||
{
|
||||
_ = Common.Retry(
|
||||
_ = IpcChannelHelper.Retry(
|
||||
nameof(SystemClipboard.SetImage),
|
||||
() =>
|
||||
{
|
||||
@@ -568,7 +568,7 @@ WellKnownSidType.AuthenticatedUserSid, null);
|
||||
{
|
||||
try
|
||||
{
|
||||
_ = Common.Retry(
|
||||
_ = IpcChannelHelper.Retry(
|
||||
nameof(SystemClipboard.SetText),
|
||||
() =>
|
||||
{
|
||||
@@ -600,44 +600,4 @@ WellKnownSidType.AuthenticatedUserSid, null);
|
||||
{
|
||||
internal const int QUIT_CMD = 0x409;
|
||||
}
|
||||
|
||||
internal sealed partial class Common
|
||||
{
|
||||
internal static bool IpcChannelCreated { get; set; }
|
||||
|
||||
internal static T Retry<T>(string name, Func<T> func, Action<string> log, Action preRetry = null)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
do
|
||||
{
|
||||
try
|
||||
{
|
||||
T rv = func();
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
log($"Trace: {name} has been successful after {count} retry.");
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
count++;
|
||||
|
||||
preRetry?.Invoke();
|
||||
|
||||
if (count > 10)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
Application.DoEvents();
|
||||
Thread.Sleep(200);
|
||||
}
|
||||
}
|
||||
while (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1036,7 +1036,7 @@ internal static class Clipboard
|
||||
{
|
||||
try
|
||||
{
|
||||
_ = Common.Retry(
|
||||
_ = IpcChannelHelper.Retry(
|
||||
nameof(SystemClipboard.SetFileDropList),
|
||||
() =>
|
||||
{
|
||||
@@ -1073,7 +1073,7 @@ internal static class Clipboard
|
||||
{
|
||||
try
|
||||
{
|
||||
_ = Common.Retry(
|
||||
_ = IpcChannelHelper.Retry(
|
||||
nameof(SystemClipboard.SetImage),
|
||||
() =>
|
||||
{
|
||||
@@ -1104,7 +1104,7 @@ internal static class Clipboard
|
||||
{
|
||||
try
|
||||
{
|
||||
_ = Common.Retry(
|
||||
_ = IpcChannelHelper.Retry(
|
||||
nameof(SystemClipboard.SetText),
|
||||
() =>
|
||||
{
|
||||
|
||||
1654
src/modules/MouseWithoutBorders/App/Core/Common.cs
Normal file
1654
src/modules/MouseWithoutBorders/App/Core/Common.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -295,9 +295,9 @@ internal static class Helper
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Common.IpcChannelCreated)
|
||||
if (!IpcChannelHelper.IpcChannelCreated)
|
||||
{
|
||||
Logger.TelemetryLogTrace($"{nameof(Common.IpcChannelCreated)} = {Common.IpcChannelCreated}. {Logger.GetStackTrace(new StackTrace())}", SeverityLevel.Warning);
|
||||
Logger.TelemetryLogTrace($"{nameof(IpcChannelHelper.IpcChannelCreated)} = {IpcChannelHelper.IpcChannelCreated}. {Logger.GetStackTrace(new StackTrace())}", SeverityLevel.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
53
src/modules/MouseWithoutBorders/App/Core/IpcChannelHelper.cs
Normal file
53
src/modules/MouseWithoutBorders/App/Core/IpcChannelHelper.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
// 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.Threading;
|
||||
using System.Windows.Forms;
|
||||
|
||||
#if !MM_HELPER
|
||||
using Thread = MouseWithoutBorders.Core.Thread;
|
||||
#endif
|
||||
|
||||
namespace MouseWithoutBorders.Core;
|
||||
|
||||
internal static class IpcChannelHelper
|
||||
{
|
||||
internal static bool IpcChannelCreated { get; set; }
|
||||
|
||||
internal static T Retry<T>(string name, Func<T> func, Action<string> log, Action preRetry = null)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
do
|
||||
{
|
||||
try
|
||||
{
|
||||
T rv = func();
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
log($"Trace: {name} has been successful after {count} retry.");
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
count++;
|
||||
|
||||
preRetry?.Invoke();
|
||||
|
||||
if (count > 10)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
Application.DoEvents();
|
||||
Thread.Sleep(200);
|
||||
}
|
||||
}
|
||||
while (true);
|
||||
}
|
||||
}
|
||||
@@ -198,7 +198,6 @@ internal static class Logger
|
||||
}
|
||||
|
||||
Logger.DumpProgramLogs(sb, level);
|
||||
Logger.DumpOtherLogs(sb, level);
|
||||
Logger.DumpStaticTypes(sb, level);
|
||||
|
||||
log = string.Format(
|
||||
@@ -240,19 +239,16 @@ internal static class Logger
|
||||
_ = Logger.PrivateDump(sb, AllLogs, "[Program logs]\r\n===============\r\n", 0, level, false);
|
||||
}
|
||||
|
||||
internal static void DumpOtherLogs(StringBuilder sb, int level)
|
||||
{
|
||||
_ = Logger.PrivateDump(sb, new Common(), "[Other Logs]\r\n===============\r\n", 0, level, false);
|
||||
}
|
||||
|
||||
internal static void DumpStaticTypes(StringBuilder sb, int level)
|
||||
{
|
||||
var staticTypes = new List<Type>
|
||||
{
|
||||
typeof(Clipboard),
|
||||
typeof(Common),
|
||||
typeof(DragDrop),
|
||||
typeof(Encryption),
|
||||
typeof(Event),
|
||||
typeof(IpcChannelHelper),
|
||||
typeof(InitAndCleanup),
|
||||
typeof(Helper),
|
||||
typeof(Launch),
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using MouseWithoutBorders.Core;
|
||||
|
||||
namespace MouseWithoutBorders
|
||||
{
|
||||
public partial class SetupPage2b : SettingsFormPage
|
||||
|
||||
@@ -15,6 +15,8 @@ using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using MouseWithoutBorders.Core;
|
||||
|
||||
namespace MouseWithoutBorders
|
||||
{
|
||||
internal partial class FrmAbout : System.Windows.Forms.Form, IDisposable
|
||||
|
||||
@@ -6,6 +6,8 @@ using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using MouseWithoutBorders.Core;
|
||||
|
||||
namespace MouseWithoutBorders
|
||||
{
|
||||
public partial class FrmMessage : System.Windows.Forms.Form
|
||||
|
||||
@@ -6,6 +6,7 @@ using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
using MouseWithoutBorders.Class;
|
||||
using MouseWithoutBorders.Core;
|
||||
|
||||
namespace MouseWithoutBorders
|
||||
{
|
||||
|
||||
@@ -49,6 +49,9 @@
|
||||
<Compile Include="..\Class\IClipboardHelper.cs">
|
||||
<Link>IClipboardHelper.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\Core\IpcChannelHelper.cs">
|
||||
<Link>IpcChannelHelper.cs</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,9 +1,38 @@
|
||||
[Program logs]
|
||||
===============
|
||||
= System.String[]
|
||||
[Other Logs]
|
||||
[Clipboard]
|
||||
===============
|
||||
Comma = System.Char[]
|
||||
--System.Char[] = System.Char[]: N/A
|
||||
Star = System.Char[]
|
||||
--System.Char[] = System.Char[]: N/A
|
||||
NullSeparator = System.Char[]
|
||||
--System.Char[] = System.Char[]: N/A
|
||||
lastClipboardEventTime = 0
|
||||
clipboardCopiedTime = 0
|
||||
<LastIDWithClipboardData>k__BackingField = NONE
|
||||
<NextClipboardViewer>k__BackingField = 0
|
||||
<IsClipboardDataImage>k__BackingField = False
|
||||
lastClipboardObject =
|
||||
<HasSwitchedMachineSinceLastCopy>k__BackingField = False
|
||||
ClipboardThreadOldLock = Lock
|
||||
--_owningThreadId = 0
|
||||
--_state = 0
|
||||
--_recursionCount = 0
|
||||
--_spinCount = 22
|
||||
--_waiterStartTimeMs = 0
|
||||
--s_contentionCount = 0
|
||||
--s_maxSpinCount = 22
|
||||
--s_minSpinCountForAdaptiveSpin = -100
|
||||
BIG_CLIPBOARD_DATA_TIMEOUT = 30000
|
||||
MAX_CLIPBOARD_DATA_SIZE_CAN_BE_SENT_INSTANTLY_TCP = 1048576
|
||||
MAX_CLIPBOARD_FILE_SIZE_CAN_BE_SENT = 104857600
|
||||
TEXT_HEADER_SIZE = 12
|
||||
DATA_SIZE = 48
|
||||
TEXT_TYPE_SEP = {4CFF57F7-BEDD-43d5-AE8F-27A61E886F2F}
|
||||
[Common]
|
||||
===============
|
||||
= MouseWithoutBorders.Common
|
||||
screenWidth = 0
|
||||
screenHeight = 0
|
||||
lastX = 0
|
||||
@@ -46,7 +75,6 @@ avgSendTime = 0
|
||||
maxSendTime = 0
|
||||
totalSendCount = 0
|
||||
totalSendTime = 0
|
||||
<IpcChannelCreated>k__BackingField = False
|
||||
TOGGLE_ICONS_SIZE = 4
|
||||
ICON_ONE = 0
|
||||
ICON_ALL = 1
|
||||
@@ -55,36 +83,6 @@ ICON_BIG_CLIPBOARD = 3
|
||||
ICON_ERROR = 4
|
||||
JUST_GOT_BACK_FROM_SCREEN_SAVER = 9999
|
||||
NETWORK_STREAM_BUF_SIZE = 1048576
|
||||
[Clipboard]
|
||||
===============
|
||||
Comma = System.Char[]
|
||||
--System.Char[] = System.Char[]: N/A
|
||||
Star = System.Char[]
|
||||
--System.Char[] = System.Char[]: N/A
|
||||
NullSeparator = System.Char[]
|
||||
--System.Char[] = System.Char[]: N/A
|
||||
lastClipboardEventTime = 0
|
||||
clipboardCopiedTime = 0
|
||||
<LastIDWithClipboardData>k__BackingField = NONE
|
||||
<NextClipboardViewer>k__BackingField = 0
|
||||
<IsClipboardDataImage>k__BackingField = False
|
||||
lastClipboardObject =
|
||||
<HasSwitchedMachineSinceLastCopy>k__BackingField = False
|
||||
ClipboardThreadOldLock = Lock
|
||||
--_owningThreadId = 0
|
||||
--_state = 0
|
||||
--_recursionCount = 0
|
||||
--_spinCount = 22
|
||||
--_waiterStartTimeMs = 0
|
||||
--s_contentionCount = 0
|
||||
--s_maxSpinCount = 22
|
||||
--s_minSpinCountForAdaptiveSpin = -100
|
||||
BIG_CLIPBOARD_DATA_TIMEOUT = 30000
|
||||
MAX_CLIPBOARD_DATA_SIZE_CAN_BE_SENT_INSTANTLY_TCP = 1048576
|
||||
MAX_CLIPBOARD_FILE_SIZE_CAN_BE_SENT = 104857600
|
||||
TEXT_HEADER_SIZE = 12
|
||||
DATA_SIZE = 48
|
||||
TEXT_TYPE_SEP = {4CFF57F7-BEDD-43d5-AE8F-27A61E886F2F}
|
||||
[DragDrop]
|
||||
===============
|
||||
isDragging = False
|
||||
@@ -174,6 +172,9 @@ actualLastPos = {X=0,Y=0}
|
||||
--Empty = {X=0,Y=0}
|
||||
myLastX = 0
|
||||
myLastY = 0
|
||||
[IpcChannelHelper]
|
||||
===============
|
||||
<IpcChannelCreated>k__BackingField = False
|
||||
[InitAndCleanup]
|
||||
===============
|
||||
initDone = False
|
||||
@@ -440,6 +441,7 @@ WM_LBUTTONDBLCLK = 515
|
||||
WM_RBUTTONDBLCLK = 518
|
||||
WM_MBUTTONDBLCLK = 521
|
||||
WM_MOUSEWHEEL = 522
|
||||
WM_MOUSEHWHEEL = 526
|
||||
WM_KEYDOWN = 256
|
||||
WM_KEYUP = 257
|
||||
WM_SYSKEYDOWN = 260
|
||||
|
||||
@@ -117,7 +117,6 @@ public static class LoggerTests
|
||||
// copied from DumpObjects in Logger.cs
|
||||
var sb = new StringBuilder(1000000);
|
||||
Logger.DumpProgramLogs(sb, settingsDumpObjectsLevel);
|
||||
Logger.DumpOtherLogs(sb, settingsDumpObjectsLevel);
|
||||
Logger.DumpStaticTypes(sb, settingsDumpObjectsLevel);
|
||||
var actual = sb.ToString();
|
||||
|
||||
|
||||
@@ -28,6 +28,13 @@ namespace Awake.Core.Native
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool AllocConsole();
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
internal static extern bool AttachConsole(int dwProcessId);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern void FreeConsole();
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
internal static extern bool SetStdHandle(int nStdHandle, IntPtr hHandle);
|
||||
|
||||
|
||||
@@ -49,5 +49,8 @@ namespace Awake.Core.Native
|
||||
// Menu Item Info Flags
|
||||
internal const uint MNS_AUTO_DISMISS = 0x10000000;
|
||||
internal const uint MIM_STYLE = 0x00000010;
|
||||
|
||||
// Attach Console
|
||||
internal const int ATTACH_PARENT_PROCESS = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,24 @@ namespace Awake
|
||||
|
||||
private static async Task<int> Main(string[] args)
|
||||
{
|
||||
var rootCommand = BuildRootCommand();
|
||||
|
||||
Bridge.AttachConsole(Core.Native.Constants.ATTACH_PARENT_PROCESS);
|
||||
|
||||
var parseResult = rootCommand.Parse(args);
|
||||
|
||||
if (parseResult.Tokens.Any(t => t.Value.ToLowerInvariant() is "--help" or "-h" or "-?"))
|
||||
{
|
||||
// Print help and exit.
|
||||
return rootCommand.Invoke(args);
|
||||
}
|
||||
|
||||
if (parseResult.Errors.Count > 0)
|
||||
{
|
||||
// Shows errors and returns non-zero.
|
||||
return rootCommand.Invoke(args);
|
||||
}
|
||||
|
||||
_settingsUtils = SettingsUtils.Default;
|
||||
|
||||
LockMutex = new Mutex(true, Core.Constants.AppName, out bool instantiated);
|
||||
@@ -107,116 +125,97 @@ namespace Awake
|
||||
Bridge.GetPwrCapabilities(out _powerCapabilities);
|
||||
Logger.LogInfo(JsonSerializer.Serialize(_powerCapabilities, _serializerOptions));
|
||||
|
||||
Logger.LogInfo("Parsing parameters...");
|
||||
|
||||
Option<bool> configOption = new(_aliasesConfigOption, () => false, Resources.AWAKE_CMD_HELP_CONFIG_OPTION)
|
||||
{
|
||||
Arity = ArgumentArity.ZeroOrOne,
|
||||
IsRequired = false,
|
||||
};
|
||||
|
||||
Option<bool> displayOption = new(_aliasesDisplayOption, () => true, Resources.AWAKE_CMD_HELP_DISPLAY_OPTION)
|
||||
{
|
||||
Arity = ArgumentArity.ZeroOrOne,
|
||||
IsRequired = false,
|
||||
};
|
||||
|
||||
Option<uint> timeOption = new(_aliasesTimeOption, () => 0, Resources.AWAKE_CMD_HELP_TIME_OPTION)
|
||||
{
|
||||
Arity = ArgumentArity.ExactlyOne,
|
||||
IsRequired = false,
|
||||
};
|
||||
|
||||
Option<int> pidOption = new(_aliasesPidOption, () => 0, Resources.AWAKE_CMD_HELP_PID_OPTION)
|
||||
{
|
||||
Arity = ArgumentArity.ZeroOrOne,
|
||||
IsRequired = false,
|
||||
};
|
||||
|
||||
Option<string> expireAtOption = new(_aliasesExpireAtOption, () => string.Empty, Resources.AWAKE_CMD_HELP_EXPIRE_AT_OPTION)
|
||||
{
|
||||
Arity = ArgumentArity.ZeroOrOne,
|
||||
IsRequired = false,
|
||||
};
|
||||
|
||||
Option<bool> parentPidOption = new(_aliasesParentPidOption, () => false, Resources.AWAKE_CMD_PARENT_PID_OPTION)
|
||||
{
|
||||
Arity = ArgumentArity.ZeroOrOne,
|
||||
IsRequired = false,
|
||||
};
|
||||
|
||||
timeOption.AddValidator(result =>
|
||||
{
|
||||
if (result.Tokens.Count != 0 && !uint.TryParse(result.Tokens[0].Value, out _))
|
||||
{
|
||||
string errorMessage = $"Interval in --time-limit could not be parsed correctly. Check that the value is valid and doesn't exceed 4,294,967,295. Value used: {result.Tokens[0].Value}.";
|
||||
Logger.LogError(errorMessage);
|
||||
result.ErrorMessage = errorMessage;
|
||||
}
|
||||
});
|
||||
|
||||
pidOption.AddValidator(result =>
|
||||
{
|
||||
if (result.Tokens.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string tokenValue = result.Tokens[0].Value;
|
||||
|
||||
if (!int.TryParse(tokenValue, out int parsed))
|
||||
{
|
||||
string errorMessage = $"PID value in --pid could not be parsed correctly. Check that the value is valid and falls within the boundaries of Windows PID process limits. Value used: {tokenValue}.";
|
||||
Logger.LogError(errorMessage);
|
||||
result.ErrorMessage = errorMessage;
|
||||
return;
|
||||
}
|
||||
|
||||
if (parsed <= 0)
|
||||
{
|
||||
string errorMessage = $"PID value in --pid must be a positive integer. Value used: {parsed}.";
|
||||
Logger.LogError(errorMessage);
|
||||
result.ErrorMessage = errorMessage;
|
||||
return;
|
||||
}
|
||||
|
||||
// Process existence check. (We also re-validate just before binding.)
|
||||
if (!ProcessExists(parsed))
|
||||
{
|
||||
string errorMessage = $"No running process found with an ID of {parsed}.";
|
||||
Logger.LogError(errorMessage);
|
||||
result.ErrorMessage = errorMessage;
|
||||
}
|
||||
});
|
||||
|
||||
expireAtOption.AddValidator(result =>
|
||||
{
|
||||
if (result.Tokens.Count != 0 && !DateTimeOffset.TryParse(result.Tokens[0].Value, out _))
|
||||
{
|
||||
string errorMessage = $"Date and time value in --expire-at could not be parsed correctly. Check that the value is valid date and time. Refer to https://aka.ms/powertoys/awake for format examples. Value used: {result.Tokens[0].Value}.";
|
||||
Logger.LogError(errorMessage);
|
||||
result.ErrorMessage = errorMessage;
|
||||
}
|
||||
});
|
||||
|
||||
RootCommand? rootCommand =
|
||||
[
|
||||
configOption,
|
||||
displayOption,
|
||||
timeOption,
|
||||
pidOption,
|
||||
expireAtOption,
|
||||
parentPidOption,
|
||||
];
|
||||
|
||||
rootCommand.Description = Core.Constants.AppName;
|
||||
rootCommand.SetHandler(HandleCommandLineArguments, configOption, displayOption, timeOption, pidOption, expireAtOption, parentPidOption);
|
||||
|
||||
return rootCommand.InvokeAsync(args).Result;
|
||||
return await rootCommand.InvokeAsync(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static RootCommand BuildRootCommand()
|
||||
{
|
||||
Logger.LogInfo("Parsing parameters...");
|
||||
|
||||
Option<bool> configOption = new(_aliasesConfigOption, () => false, Resources.AWAKE_CMD_HELP_CONFIG_OPTION)
|
||||
{
|
||||
Arity = ArgumentArity.ZeroOrOne,
|
||||
IsRequired = false,
|
||||
};
|
||||
|
||||
Option<bool> displayOption = new(_aliasesDisplayOption, () => true, Resources.AWAKE_CMD_HELP_DISPLAY_OPTION)
|
||||
{
|
||||
Arity = ArgumentArity.ZeroOrOne,
|
||||
IsRequired = false,
|
||||
};
|
||||
|
||||
Option<uint> timeOption = new(_aliasesTimeOption, () => 0, Resources.AWAKE_CMD_HELP_TIME_OPTION)
|
||||
{
|
||||
Arity = ArgumentArity.ExactlyOne,
|
||||
IsRequired = false,
|
||||
};
|
||||
|
||||
Option<int> pidOption = new(_aliasesPidOption, () => 0, Resources.AWAKE_CMD_HELP_PID_OPTION)
|
||||
{
|
||||
Arity = ArgumentArity.ZeroOrOne,
|
||||
IsRequired = false,
|
||||
};
|
||||
|
||||
Option<string> expireAtOption = new(_aliasesExpireAtOption, () => string.Empty, Resources.AWAKE_CMD_HELP_EXPIRE_AT_OPTION)
|
||||
{
|
||||
Arity = ArgumentArity.ZeroOrOne,
|
||||
IsRequired = false,
|
||||
};
|
||||
|
||||
Option<bool> parentPidOption = new(_aliasesParentPidOption, () => false, Resources.AWAKE_CMD_PARENT_PID_OPTION)
|
||||
{
|
||||
Arity = ArgumentArity.ZeroOrOne,
|
||||
IsRequired = false,
|
||||
};
|
||||
|
||||
timeOption.AddValidator(result =>
|
||||
{
|
||||
if (result.Tokens.Count != 0 && !uint.TryParse(result.Tokens[0].Value, out _))
|
||||
{
|
||||
string errorMessage = $"Interval in --time-limit could not be parsed correctly. Check that the value is valid and doesn't exceed 4,294,967,295. Value used: {result.Tokens[0].Value}.";
|
||||
Logger.LogError(errorMessage);
|
||||
result.ErrorMessage = errorMessage;
|
||||
}
|
||||
});
|
||||
|
||||
pidOption.AddValidator(result =>
|
||||
{
|
||||
if (result.Tokens.Count != 0 && !int.TryParse(result.Tokens[0].Value, out _))
|
||||
{
|
||||
string errorMessage = $"PID value in --pid could not be parsed correctly. Check that the value is valid and falls within the boundaries of Windows PID process limits. Value used: {result.Tokens[0].Value}.";
|
||||
Logger.LogError(errorMessage);
|
||||
result.ErrorMessage = errorMessage;
|
||||
}
|
||||
});
|
||||
|
||||
expireAtOption.AddValidator(result =>
|
||||
{
|
||||
if (result.Tokens.Count != 0 && !DateTimeOffset.TryParse(result.Tokens[0].Value, out _))
|
||||
{
|
||||
string errorMessage = $"Date and time value in --expire-at could not be parsed correctly. Check that the value is valid date and time. Refer to https://aka.ms/powertoys/awake for format examples. Value used: {result.Tokens[0].Value}.";
|
||||
Logger.LogError(errorMessage);
|
||||
result.ErrorMessage = errorMessage;
|
||||
}
|
||||
});
|
||||
|
||||
RootCommand? rootCommand =
|
||||
[
|
||||
configOption,
|
||||
displayOption,
|
||||
timeOption,
|
||||
pidOption,
|
||||
expireAtOption,
|
||||
parentPidOption,
|
||||
];
|
||||
|
||||
rootCommand.Description = Core.Constants.AppName;
|
||||
rootCommand.SetHandler(HandleCommandLineArguments, configOption, displayOption, timeOption, pidOption, expireAtOption, parentPidOption);
|
||||
|
||||
return rootCommand;
|
||||
}
|
||||
|
||||
private static void AwakeUnhandledExceptionCatcher(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
if (e.ExceptionObject is Exception exception)
|
||||
@@ -264,6 +263,7 @@ namespace Awake
|
||||
if (pid == 0 && !useParentPid)
|
||||
{
|
||||
Logger.LogInfo("No PID specified. Allocating console...");
|
||||
Bridge.FreeConsole();
|
||||
AllocateLocalConsole();
|
||||
}
|
||||
else
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
<controls:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left">
|
||||
<StackPanel Margin="-12,0,0,0" Orientation="Vertical">
|
||||
<HyperlinkButton x:Uid="Settings_GeneralPage_About_GithubLink_Hyperlink" NavigateUri="https://go.microsoft.com/fwlink/?linkid=2310837" />
|
||||
<HyperlinkButton x:Uid="Settings_GeneralPage_About_SDKDocs_Hyperlink" NavigateUri="https://go.microsoft.com/fwlink/?linkid=2310639" />
|
||||
<HyperlinkButton x:Uid="Settings_GeneralPage_About_SDKDocs_Hyperlink" NavigateUri="https://aka.ms/cmdpalextensions-devdocs" />
|
||||
</StackPanel>
|
||||
</controls:SettingsCard>
|
||||
</controls:SettingsExpander.Items>
|
||||
|
||||
@@ -8,6 +8,7 @@ using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
|
||||
namespace PowerToysExtension.Pages;
|
||||
|
||||
@@ -24,15 +25,15 @@ internal sealed partial class FancyZonesMonitorListItem : ListItem
|
||||
|
||||
var pickerPage = new FancyZonesMonitorLayoutPickerPage(monitor)
|
||||
{
|
||||
Name = "Set active layout",
|
||||
Name = Resources.FancyZones_SetActiveLayout,
|
||||
};
|
||||
|
||||
MoreCommands =
|
||||
[
|
||||
new CommandContextItem(pickerPage)
|
||||
{
|
||||
Title = "Set active layout",
|
||||
Subtitle = "Pick a layout for this monitor",
|
||||
Title = Resources.FancyZones_SetActiveLayout,
|
||||
Subtitle = Resources.FancyZones_PickLayoutForMonitor,
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -42,14 +43,14 @@ internal sealed partial class FancyZonesMonitorListItem : ListItem
|
||||
var currentVirtualDesktop = FancyZonesVirtualDesktop.GetCurrentVirtualDesktopIdString();
|
||||
var tags = new List<IDetailsElement>
|
||||
{
|
||||
DetailTag("Monitor", monitor.Data.Monitor),
|
||||
DetailTag("Instance", monitor.Data.MonitorInstanceId),
|
||||
DetailTag("Serial", monitor.Data.MonitorSerialNumber),
|
||||
DetailTag("Number", monitor.Data.MonitorNumber.ToString(CultureInfo.InvariantCulture)),
|
||||
DetailTag("Virtual desktop", currentVirtualDesktop),
|
||||
DetailTag("Work area", $"{monitor.Data.LeftCoordinate},{monitor.Data.TopCoordinate} {monitor.Data.WorkAreaWidth}\u00D7{monitor.Data.WorkAreaHeight}"),
|
||||
DetailTag("Resolution", $"{monitor.Data.MonitorWidth}\u00D7{monitor.Data.MonitorHeight}"),
|
||||
DetailTag("DPI", monitor.Data.Dpi.ToString(CultureInfo.InvariantCulture)),
|
||||
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_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)),
|
||||
};
|
||||
|
||||
return new Details
|
||||
@@ -68,7 +69,7 @@ internal sealed partial class FancyZonesMonitorListItem : ListItem
|
||||
Key = key,
|
||||
Data = new DetailsTags
|
||||
{
|
||||
Tags = [new Tag(string.IsNullOrWhiteSpace(value) ? "n/a" : value)],
|
||||
Tags = [new Tag(string.IsNullOrWhiteSpace(value) ? Resources.Common_NotAvailable : value)],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,15 +5,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
using WorkspacesCsharpLibrary.Data;
|
||||
|
||||
namespace PowerToysExtension.Commands;
|
||||
|
||||
internal sealed partial class WorkspaceListItem : ListItem
|
||||
{
|
||||
private static readonly CompositeFormat ApplicationsFormat = CompositeFormat.Parse(Resources.Workspaces_Applications_Format);
|
||||
private static readonly CompositeFormat LastLaunchedFormat = CompositeFormat.Parse(Resources.Workspaces_LastLaunched_Format);
|
||||
private static readonly CompositeFormat ApplicationsCountFormat = CompositeFormat.Parse(Resources.Workspaces_ApplicationsCount_Format);
|
||||
private static readonly CompositeFormat MinAgoFormat = CompositeFormat.Parse(Resources.Workspaces_MinAgo_Format);
|
||||
private static readonly CompositeFormat HrAgoFormat = CompositeFormat.Parse(Resources.Workspaces_HrAgo_Format);
|
||||
private static readonly CompositeFormat DaysAgoFormat = CompositeFormat.Parse(Resources.Workspaces_DaysAgo_Format);
|
||||
|
||||
public WorkspaceListItem(ProjectWrapper workspace, IconInfo icon)
|
||||
: base(new LaunchWorkspaceCommand(workspace.Id))
|
||||
{
|
||||
@@ -28,13 +37,13 @@ internal sealed partial class WorkspaceListItem : ListItem
|
||||
var appCount = workspace.Applications?.Count ?? 0;
|
||||
var appsText = appCount switch
|
||||
{
|
||||
0 => "No applications",
|
||||
_ => string.Format(CultureInfo.CurrentCulture, "{0} applications", appCount),
|
||||
0 => Resources.Workspaces_NoApplications,
|
||||
_ => string.Format(CultureInfo.CurrentCulture, ApplicationsFormat, appCount),
|
||||
};
|
||||
|
||||
var lastLaunched = workspace.LastLaunchedTime > 0
|
||||
? $"Last launched {FormatRelativeTime(workspace.LastLaunchedTime)}"
|
||||
: "Never launched";
|
||||
? string.Format(CultureInfo.CurrentCulture, LastLaunchedFormat, FormatRelativeTime(workspace.LastLaunchedTime))
|
||||
: Resources.Workspaces_NeverLaunched;
|
||||
|
||||
return $"{appsText} \u2022 {lastLaunched}";
|
||||
}
|
||||
@@ -44,15 +53,15 @@ internal sealed partial class WorkspaceListItem : ListItem
|
||||
var appCount = workspace.Applications?.Count ?? 0;
|
||||
var body = appCount switch
|
||||
{
|
||||
0 => "No applications in this workspace",
|
||||
1 => "1 application",
|
||||
_ => $"{appCount} applications",
|
||||
0 => Resources.Workspaces_NoApplicationsInWorkspace,
|
||||
1 => Resources.Workspaces_OneApplication,
|
||||
_ => string.Format(CultureInfo.CurrentCulture, ApplicationsCountFormat, appCount),
|
||||
};
|
||||
|
||||
return new Details
|
||||
{
|
||||
HeroImage = icon,
|
||||
Title = workspace.Name ?? "Workspace",
|
||||
Title = workspace.Name ?? Resources.Workspaces_Workspace,
|
||||
Body = body,
|
||||
Metadata = BuildAppMetadata(workspace),
|
||||
};
|
||||
@@ -68,7 +77,7 @@ internal sealed partial class WorkspaceListItem : ListItem
|
||||
var elements = new List<IDetailsElement>();
|
||||
foreach (var app in workspace.Applications)
|
||||
{
|
||||
var appName = string.IsNullOrWhiteSpace(app.Application) ? "App" : app.Application;
|
||||
var appName = string.IsNullOrWhiteSpace(app.Application) ? Resources.Workspaces_App : app.Application;
|
||||
var title = string.IsNullOrWhiteSpace(app.Title) ? appName : app.Title;
|
||||
|
||||
var tags = new List<ITag>();
|
||||
@@ -99,19 +108,19 @@ internal sealed partial class WorkspaceListItem : ListItem
|
||||
|
||||
if (delta.TotalMinutes < 1)
|
||||
{
|
||||
return "just now";
|
||||
return Resources.Workspaces_JustNow;
|
||||
}
|
||||
|
||||
if (delta.TotalMinutes < 60)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, "{0} min ago", (int)delta.TotalMinutes);
|
||||
return string.Format(CultureInfo.CurrentCulture, MinAgoFormat, (int)delta.TotalMinutes);
|
||||
}
|
||||
|
||||
if (delta.TotalHours < 24)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, "{0} hr ago", (int)delta.TotalHours);
|
||||
return string.Format(CultureInfo.CurrentCulture, HrAgoFormat, (int)delta.TotalHours);
|
||||
}
|
||||
|
||||
return string.Format(CultureInfo.CurrentCulture, "{0} days ago", (int)delta.TotalDays);
|
||||
return string.Format(CultureInfo.CurrentCulture, DaysAgoFormat, (int)delta.TotalDays);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,16 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
using FancyZonesEditorCommon.Data;
|
||||
using FancyZonesEditorCommon.Utils;
|
||||
using ManagedCommon;
|
||||
using PowerToysExtension.Properties;
|
||||
|
||||
using FZPaths = FancyZonesEditorCommon.Data.FancyZonesPaths;
|
||||
|
||||
@@ -20,6 +23,15 @@ internal static class FancyZonesDataService
|
||||
{
|
||||
private const string ZeroUuid = "{00000000-0000-0000-0000-000000000000}";
|
||||
|
||||
private static readonly CompositeFormat ReadMonitorDataFailedFormat = CompositeFormat.Parse(Resources.FancyZones_ReadMonitorDataFailed_Format);
|
||||
private static readonly CompositeFormat WriteAppliedLayoutsFailedFormat = CompositeFormat.Parse(Resources.FancyZones_WriteAppliedLayoutsFailed_Format);
|
||||
private static readonly CompositeFormat LayoutAppliedNotifyFailedFormat = CompositeFormat.Parse(Resources.FancyZones_LayoutAppliedNotifyFailed_Format);
|
||||
private static readonly CompositeFormat TemplateFormat = CompositeFormat.Parse(Resources.FancyZones_Template_Format);
|
||||
private static readonly CompositeFormat ZonesFormat = CompositeFormat.Parse(Resources.FancyZones_Zones_Format);
|
||||
private static readonly CompositeFormat CustomGridZonesFormat = CompositeFormat.Parse(Resources.FancyZones_CustomGrid_Zones_Format);
|
||||
private static readonly CompositeFormat CustomCanvasZonesFormat = CompositeFormat.Parse(Resources.FancyZones_CustomCanvas_Zones_Format);
|
||||
private static readonly CompositeFormat CustomZonesFormat = CompositeFormat.Parse(Resources.FancyZones_Custom_Zones_Format);
|
||||
|
||||
public static bool TryGetMonitors(out IReadOnlyList<FancyZonesMonitorDescriptor> monitors, out string error)
|
||||
{
|
||||
monitors = Array.Empty<FancyZonesMonitorDescriptor>();
|
||||
@@ -31,7 +43,7 @@ internal static class FancyZonesDataService
|
||||
{
|
||||
if (!File.Exists(FZPaths.EditorParameters))
|
||||
{
|
||||
error = "FancyZones monitor data not found. Open FancyZones Editor once to initialize.";
|
||||
error = Resources.FancyZones_MonitorDataNotFound;
|
||||
Logger.LogWarning($"TryGetMonitors: File not found. Path={FZPaths.EditorParameters}");
|
||||
return false;
|
||||
}
|
||||
@@ -43,7 +55,7 @@ internal static class FancyZonesDataService
|
||||
var editorMonitors = editorParams.Monitors;
|
||||
if (editorMonitors is null || editorMonitors.Count == 0)
|
||||
{
|
||||
error = "No FancyZones monitors found.";
|
||||
error = Resources.FancyZones_NoFancyZonesMonitorsFound;
|
||||
Logger.LogWarning($"TryGetMonitors: No monitors in file.");
|
||||
return false;
|
||||
}
|
||||
@@ -56,7 +68,7 @@ internal static class FancyZonesDataService
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = $"Failed to read FancyZones monitor data: {ex.Message}";
|
||||
error = string.Format(CultureInfo.CurrentCulture, ReadMonitorDataFailedFormat, ex.Message);
|
||||
Logger.LogError($"TryGetMonitors: Exception. Message={ex.Message} Stack={ex.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
@@ -204,7 +216,7 @@ internal static class FancyZonesDataService
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (false, $"Failed to write applied layouts: {ex.Message}");
|
||||
return (false, string.Format(CultureInfo.CurrentCulture, WriteAppliedLayoutsFailedFormat, ex.Message));
|
||||
}
|
||||
|
||||
try
|
||||
@@ -213,10 +225,10 @@ internal static class FancyZonesDataService
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (true, $"Layout applied, but FancyZones could not be notified: {ex.Message}");
|
||||
return (true, string.Format(CultureInfo.CurrentCulture, LayoutAppliedNotifyFailedFormat, ex.Message));
|
||||
}
|
||||
|
||||
return (true, "Layout applied.");
|
||||
return (true, Resources.FancyZones_LayoutApplied);
|
||||
}
|
||||
|
||||
private static AppliedLayouts.AppliedLayoutWrapper? FindAppliedLayoutEntry(AppliedLayouts.AppliedLayoutsListWrapper file, EditorParameters.NativeMonitorDataWrapper monitor, string virtualDesktopId)
|
||||
@@ -293,8 +305,8 @@ internal static class FancyZonesDataService
|
||||
var zoneCount = type.Equals("blank", StringComparison.OrdinalIgnoreCase)
|
||||
? 0
|
||||
: template.ZoneCount > 0 ? template.ZoneCount : 3;
|
||||
var title = $"Template: {type}";
|
||||
var subtitle = $"{zoneCount} zones";
|
||||
var title = string.Format(CultureInfo.CurrentCulture, TemplateFormat, type);
|
||||
var subtitle = string.Format(CultureInfo.CurrentCulture, ZonesFormat, zoneCount);
|
||||
|
||||
yield return new FancyZonesLayoutDescriptor
|
||||
{
|
||||
@@ -357,9 +369,9 @@ internal static class FancyZonesDataService
|
||||
var title = custom.Name.Trim();
|
||||
var subtitle = customType switch
|
||||
{
|
||||
"grid" => $"Custom grid \u2022 {applied.ZoneCount} zones",
|
||||
"canvas" => $"Custom canvas \u2022 {applied.ZoneCount} zones",
|
||||
_ => $"Custom \u2022 {applied.ZoneCount} zones",
|
||||
"grid" => string.Format(CultureInfo.CurrentCulture, CustomGridZonesFormat, applied.ZoneCount),
|
||||
"canvas" => string.Format(CultureInfo.CurrentCulture, CustomCanvasZonesFormat, applied.ZoneCount),
|
||||
_ => string.Format(CultureInfo.CurrentCulture, CustomZonesFormat, applied.ZoneCount),
|
||||
};
|
||||
|
||||
yield return new FancyZonesLayoutDescriptor
|
||||
|
||||
@@ -61,6 +61,21 @@
|
||||
<ProjectReference Include="..\..\..\Workspaces\Workspaces.ModuleServices\Workspaces.ModuleServices.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Always build/publish AOT so the extension ships as native code -->
|
||||
<SelfContained>true</SelfContained>
|
||||
|
||||
@@ -7,6 +7,7 @@ using Common.UI;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
|
||||
@@ -22,8 +23,8 @@ internal sealed class AdvancedPasteModuleCommandProvider : ModuleCommandProvider
|
||||
{
|
||||
yield return new ListItem(new OpenAdvancedPasteCommand())
|
||||
{
|
||||
Title = "Open Advanced Paste",
|
||||
Subtitle = "Launch the Advanced Paste UI",
|
||||
Title = Resources.AdvancedPaste_Open_Title,
|
||||
Subtitle = Resources.AdvancedPaste_Open_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
@@ -31,7 +32,7 @@ internal sealed class AdvancedPasteModuleCommandProvider : ModuleCommandProvider
|
||||
yield return new ListItem(new OpenInSettingsCommand(module, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open Advanced Paste settings",
|
||||
Subtitle = Resources.AdvancedPaste_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
using static Common.UI.SettingsDeepLink;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
@@ -20,7 +21,7 @@ internal sealed class AlwaysOnTopModuleCommandProvider : ModuleCommandProvider
|
||||
yield return new ListItem(new OpenInSettingsCommand(SettingsWindow.AlwaysOnTop, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open Always On Top settings",
|
||||
Subtitle = Resources.AlwaysOnTop_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Pages;
|
||||
using PowerToysExtension.Properties;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
|
||||
@@ -26,7 +27,7 @@ internal sealed class AwakeModuleCommandProvider : ModuleCommandProvider
|
||||
items.Add(new ListItem(new OpenInSettingsCommand(module, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open Awake settings",
|
||||
Subtitle = Resources.Awake_Settings_Subtitle,
|
||||
Icon = moduleIcon,
|
||||
});
|
||||
|
||||
@@ -49,40 +50,40 @@ internal sealed class AwakeModuleCommandProvider : ModuleCommandProvider
|
||||
|
||||
statusItem = new ListItem(new CommandItem(refreshCommand))
|
||||
{
|
||||
Title = "Awake: Current status",
|
||||
Title = Resources.Awake_Status_Title,
|
||||
Subtitle = AwakeStatusService.GetStatusSubtitle(),
|
||||
Icon = icon,
|
||||
};
|
||||
items.Add(statusItem);
|
||||
|
||||
items.Add(new ListItem(new StartAwakeCommand("Awake: Keep awake indefinitely", () => AwakeService.Instance.SetIndefiniteAsync(), "Awake set to indefinite", refreshStatus))
|
||||
items.Add(new ListItem(new StartAwakeCommand(Resources.Awake_KeepIndefinite_Title, () => AwakeService.Instance.SetIndefiniteAsync(), Resources.Awake_SetIndefinite_Toast, refreshStatus))
|
||||
{
|
||||
Title = "Awake: Keep awake indefinitely",
|
||||
Subtitle = "Run Awake in indefinite mode",
|
||||
Title = Resources.Awake_KeepIndefinite_Title,
|
||||
Subtitle = Resources.Awake_KeepIndefinite_Subtitle,
|
||||
Icon = icon,
|
||||
});
|
||||
items.Add(new ListItem(new StartAwakeCommand("Awake: Keep awake for 30 minutes", () => AwakeService.Instance.SetTimedAsync(30), "Awake set for 30 minutes", refreshStatus))
|
||||
items.Add(new ListItem(new StartAwakeCommand(Resources.Awake_Keep30Min_Title, () => AwakeService.Instance.SetTimedAsync(30), Resources.Awake_Set30Min_Toast, refreshStatus))
|
||||
{
|
||||
Title = "Awake: Keep awake for 30 minutes",
|
||||
Subtitle = "Run Awake timed for 30 minutes",
|
||||
Title = Resources.Awake_Keep30Min_Title,
|
||||
Subtitle = Resources.Awake_Keep30Min_Subtitle,
|
||||
Icon = icon,
|
||||
});
|
||||
items.Add(new ListItem(new StartAwakeCommand("Awake: Keep awake for 1 hour", () => AwakeService.Instance.SetTimedAsync(60), "Awake set for 1 hour", refreshStatus))
|
||||
items.Add(new ListItem(new StartAwakeCommand(Resources.Awake_Keep1Hour_Title, () => AwakeService.Instance.SetTimedAsync(60), Resources.Awake_Set1Hour_Toast, refreshStatus))
|
||||
{
|
||||
Title = "Awake: Keep awake for 1 hour",
|
||||
Subtitle = "Run Awake timed for 1 hour",
|
||||
Title = Resources.Awake_Keep1Hour_Title,
|
||||
Subtitle = Resources.Awake_Keep1Hour_Subtitle,
|
||||
Icon = icon,
|
||||
});
|
||||
items.Add(new ListItem(new StartAwakeCommand("Awake: Keep awake for 2 hours", () => AwakeService.Instance.SetTimedAsync(120), "Awake set for 2 hours", refreshStatus))
|
||||
items.Add(new ListItem(new StartAwakeCommand(Resources.Awake_Keep2Hours_Title, () => AwakeService.Instance.SetTimedAsync(120), Resources.Awake_Set2Hours_Toast, refreshStatus))
|
||||
{
|
||||
Title = "Awake: Keep awake for 2 hours",
|
||||
Subtitle = "Run Awake timed for 2 hours",
|
||||
Title = Resources.Awake_Keep2Hours_Title,
|
||||
Subtitle = Resources.Awake_Keep2Hours_Subtitle,
|
||||
Icon = icon,
|
||||
});
|
||||
items.Add(new ListItem(new StopAwakeCommand(refreshStatus))
|
||||
{
|
||||
Title = "Awake: Turn off",
|
||||
Subtitle = "Switch Awake back to Off",
|
||||
Title = Resources.Awake_TurnOff_Title,
|
||||
Subtitle = Resources.Awake_TurnOff_Subtitle,
|
||||
Icon = icon,
|
||||
});
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Pages;
|
||||
using PowerToysExtension.Properties;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
|
||||
@@ -24,7 +25,7 @@ internal sealed class ColorPickerModuleCommandProvider : ModuleCommandProvider
|
||||
commands.Add(new ListItem(new OpenInSettingsCommand(module, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open Color Picker settings",
|
||||
Subtitle = Resources.ColorPicker_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
});
|
||||
|
||||
@@ -36,15 +37,15 @@ internal sealed class ColorPickerModuleCommandProvider : ModuleCommandProvider
|
||||
// Direct entries in the module list.
|
||||
commands.Add(new ListItem(new OpenColorPickerCommand())
|
||||
{
|
||||
Title = "Open Color Picker",
|
||||
Subtitle = "Start a color pick session",
|
||||
Title = Resources.ColorPicker_Open_Title,
|
||||
Subtitle = Resources.ColorPicker_Open_Subtitle,
|
||||
Icon = icon,
|
||||
});
|
||||
|
||||
commands.Add(new ListItem(new CommandItem(new ColorPickerSavedColorsPage()))
|
||||
{
|
||||
Title = "Saved colors",
|
||||
Subtitle = "Browse and copy saved colors",
|
||||
Title = Resources.ColorPicker_SavedColors_Title,
|
||||
Subtitle = Resources.ColorPicker_SavedColors_Subtitle,
|
||||
Icon = icon,
|
||||
});
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
using static Common.UI.SettingsDeepLink;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
@@ -20,7 +21,7 @@ internal sealed class CommandNotFoundModuleCommandProvider : ModuleCommandProvid
|
||||
yield return new ListItem(new OpenInSettingsCommand(SettingsWindow.CmdNotFound, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open Command Not Found settings",
|
||||
Subtitle = Resources.CommandNotFound_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
using static Common.UI.SettingsDeepLink;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
@@ -22,15 +23,15 @@ internal sealed class CropAndLockModuleCommandProvider : ModuleCommandProvider
|
||||
{
|
||||
yield return new ListItem(new CropAndLockReparentCommand())
|
||||
{
|
||||
Title = "Crop and Lock (Reparent)",
|
||||
Subtitle = "Create a cropped reparented window",
|
||||
Title = Resources.CropAndLock_Reparent_Title,
|
||||
Subtitle = Resources.CropAndLock_Reparent_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
|
||||
yield return new ListItem(new CropAndLockThumbnailCommand())
|
||||
{
|
||||
Title = "Crop and Lock (Thumbnail)",
|
||||
Subtitle = "Create a cropped thumbnail window",
|
||||
Title = Resources.CropAndLock_Thumbnail_Title,
|
||||
Subtitle = Resources.CropAndLock_Thumbnail_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
@@ -38,7 +39,7 @@ internal sealed class CropAndLockModuleCommandProvider : ModuleCommandProvider
|
||||
yield return new ListItem(new OpenInSettingsCommand(module, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open Crop and Lock settings",
|
||||
Subtitle = Resources.CropAndLock_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
using static Common.UI.SettingsDeepLink;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
@@ -22,15 +23,15 @@ internal sealed class EnvironmentVariablesModuleCommandProvider : ModuleCommandP
|
||||
{
|
||||
yield return new ListItem(new OpenEnvironmentVariablesCommand())
|
||||
{
|
||||
Title = "Open Environment Variables",
|
||||
Subtitle = "Launch Environment Variables editor",
|
||||
Title = Resources.EnvironmentVariables_Open_Title,
|
||||
Subtitle = Resources.EnvironmentVariables_Open_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
|
||||
yield return new ListItem(new OpenEnvironmentVariablesAdminCommand())
|
||||
{
|
||||
Title = "Open Environment Variables (Admin)",
|
||||
Subtitle = "Launch Environment Variables editor as admin",
|
||||
Title = Resources.EnvironmentVariables_OpenAdmin_Title,
|
||||
Subtitle = Resources.EnvironmentVariables_OpenAdmin_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
@@ -38,7 +39,7 @@ internal sealed class EnvironmentVariablesModuleCommandProvider : ModuleCommandP
|
||||
yield return new ListItem(new OpenInSettingsCommand(module, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open Environment Variables settings",
|
||||
Subtitle = Resources.EnvironmentVariables_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Pages;
|
||||
using PowerToysExtension.Properties;
|
||||
using static Common.UI.SettingsDeepLink;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
@@ -23,22 +24,22 @@ internal sealed class FancyZonesModuleCommandProvider : ModuleCommandProvider
|
||||
{
|
||||
yield return new ListItem(new CommandItem(new FancyZonesLayoutsPage()))
|
||||
{
|
||||
Title = "FancyZones: Layouts",
|
||||
Subtitle = "Apply a layout to all monitors or a specific monitor",
|
||||
Title = Resources.FancyZones_Layouts_Title,
|
||||
Subtitle = Resources.FancyZones_Layouts_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
|
||||
yield return new ListItem(new CommandItem(new FancyZonesMonitorsPage()))
|
||||
{
|
||||
Title = "FancyZones: Monitors",
|
||||
Subtitle = "Identify monitors and apply layouts",
|
||||
Title = Resources.FancyZones_Monitors_Title,
|
||||
Subtitle = Resources.FancyZones_Monitors_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
|
||||
yield return new ListItem(new OpenFancyZonesEditorCommand())
|
||||
{
|
||||
Title = "Open FancyZones Editor",
|
||||
Subtitle = "Launch layout editor",
|
||||
Title = Resources.FancyZones_OpenEditor_Title,
|
||||
Subtitle = Resources.FancyZones_OpenEditor_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
@@ -46,7 +47,7 @@ internal sealed class FancyZonesModuleCommandProvider : ModuleCommandProvider
|
||||
yield return new ListItem(new OpenInSettingsCommand(module, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open FancyZones settings",
|
||||
Subtitle = Resources.FancyZones_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
using static Common.UI.SettingsDeepLink;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
@@ -20,7 +21,7 @@ internal sealed class FileExplorerAddonsModuleCommandProvider : ModuleCommandPro
|
||||
yield return new ListItem(new OpenInSettingsCommand(SettingsWindow.FileExplorer, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open File Explorer add-ons settings",
|
||||
Subtitle = Resources.FileExplorerAddons_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
using static Common.UI.SettingsDeepLink;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
@@ -20,7 +21,7 @@ internal sealed class FileLocksmithModuleCommandProvider : ModuleCommandProvider
|
||||
yield return new ListItem(new OpenInSettingsCommand(SettingsWindow.FileLocksmith, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open File Locksmith settings",
|
||||
Subtitle = Resources.FileLocksmith_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
using static Common.UI.SettingsDeepLink;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
@@ -22,15 +23,15 @@ internal sealed class HostsModuleCommandProvider : ModuleCommandProvider
|
||||
{
|
||||
yield return new ListItem(new OpenHostsEditorCommand())
|
||||
{
|
||||
Title = "Open Hosts File Editor",
|
||||
Subtitle = "Launch Hosts File Editor",
|
||||
Title = Resources.Hosts_Open_Title,
|
||||
Subtitle = Resources.Hosts_Open_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
|
||||
yield return new ListItem(new OpenHostsEditorAdminCommand())
|
||||
{
|
||||
Title = "Open Hosts File Editor (Admin)",
|
||||
Subtitle = "Launch Hosts File Editor as admin",
|
||||
Title = Resources.Hosts_OpenAdmin_Title,
|
||||
Subtitle = Resources.Hosts_OpenAdmin_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
@@ -38,7 +39,7 @@ internal sealed class HostsModuleCommandProvider : ModuleCommandProvider
|
||||
yield return new ListItem(new OpenInSettingsCommand(module, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open Hosts File Editor settings",
|
||||
Subtitle = Resources.Hosts_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
using static Common.UI.SettingsDeepLink;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
@@ -20,7 +21,7 @@ internal sealed class ImageResizerModuleCommandProvider : ModuleCommandProvider
|
||||
yield return new ListItem(new OpenInSettingsCommand(SettingsWindow.ImageResizer, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open Image Resizer settings",
|
||||
Subtitle = Resources.ImageResizer_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
using static Common.UI.SettingsDeepLink;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
@@ -20,7 +21,7 @@ internal sealed class KeyboardManagerModuleCommandProvider : ModuleCommandProvid
|
||||
yield return new ListItem(new OpenInSettingsCommand(SettingsWindow.KBM, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open Keyboard Manager settings",
|
||||
Subtitle = Resources.KeyboardManager_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
using static Common.UI.SettingsDeepLink;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
@@ -24,8 +25,8 @@ internal sealed class LightSwitchModuleCommandProvider : ModuleCommandProvider
|
||||
{
|
||||
items.Add(new ListItem(new ToggleLightSwitchCommand())
|
||||
{
|
||||
Title = "Toggle Light Switch",
|
||||
Subtitle = "Toggle system/apps theme immediately",
|
||||
Title = Resources.LightSwitch_Toggle_Title,
|
||||
Subtitle = Resources.LightSwitch_Toggle_Subtitle,
|
||||
Icon = icon,
|
||||
});
|
||||
}
|
||||
@@ -33,7 +34,7 @@ internal sealed class LightSwitchModuleCommandProvider : ModuleCommandProvider
|
||||
items.Add(new ListItem(new OpenInSettingsCommand(module, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open Light Switch settings",
|
||||
Subtitle = Resources.LightSwitch_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
});
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
using static Common.UI.SettingsDeepLink;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
@@ -22,8 +23,8 @@ internal sealed class MouseUtilsModuleCommandProvider : ModuleCommandProvider
|
||||
{
|
||||
yield return new ListItem(new ToggleFindMyMouseCommand())
|
||||
{
|
||||
Title = "Trigger Find My Mouse",
|
||||
Subtitle = "Focus the mouse pointer",
|
||||
Title = Resources.MouseUtils_FindMyMouse_Title,
|
||||
Subtitle = Resources.MouseUtils_FindMyMouse_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
@@ -32,8 +33,8 @@ internal sealed class MouseUtilsModuleCommandProvider : ModuleCommandProvider
|
||||
{
|
||||
yield return new ListItem(new ToggleMouseHighlighterCommand())
|
||||
{
|
||||
Title = "Toggle Mouse Highlighter",
|
||||
Subtitle = "Highlight mouse clicks",
|
||||
Title = Resources.MouseUtils_Highlighter_Title,
|
||||
Subtitle = Resources.MouseUtils_Highlighter_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
@@ -42,8 +43,8 @@ internal sealed class MouseUtilsModuleCommandProvider : ModuleCommandProvider
|
||||
{
|
||||
yield return new ListItem(new ToggleMouseCrosshairsCommand())
|
||||
{
|
||||
Title = "Toggle Mouse Crosshairs",
|
||||
Subtitle = "Enable or disable pointer crosshairs",
|
||||
Title = Resources.MouseUtils_Crosshairs_Title,
|
||||
Subtitle = Resources.MouseUtils_Crosshairs_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
@@ -52,8 +53,8 @@ internal sealed class MouseUtilsModuleCommandProvider : ModuleCommandProvider
|
||||
{
|
||||
yield return new ListItem(new ToggleCursorWrapCommand())
|
||||
{
|
||||
Title = "Toggle Cursor Wrap",
|
||||
Subtitle = "Wrap the cursor across monitor edges",
|
||||
Title = Resources.MouseUtils_CursorWrap_Title,
|
||||
Subtitle = Resources.MouseUtils_CursorWrap_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
@@ -62,8 +63,8 @@ internal sealed class MouseUtilsModuleCommandProvider : ModuleCommandProvider
|
||||
{
|
||||
yield return new ListItem(new ShowMouseJumpPreviewCommand())
|
||||
{
|
||||
Title = "Show Mouse Jump Preview",
|
||||
Subtitle = "Jump the pointer to a target",
|
||||
Title = Resources.MouseUtils_MouseJump_Title,
|
||||
Subtitle = Resources.MouseUtils_MouseJump_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
@@ -71,7 +72,7 @@ internal sealed class MouseUtilsModuleCommandProvider : ModuleCommandProvider
|
||||
yield return new ListItem(new OpenInSettingsCommand(module, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open Mouse Utilities settings",
|
||||
Subtitle = Resources.MouseUtils_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
using static Common.UI.SettingsDeepLink;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
@@ -20,7 +21,7 @@ internal sealed class MouseWithoutBordersModuleCommandProvider : ModuleCommandPr
|
||||
yield return new ListItem(new OpenInSettingsCommand(SettingsWindow.MouseWithoutBorders, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open Mouse Without Borders settings",
|
||||
Subtitle = Resources.MouseWithoutBorders_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
using static Common.UI.SettingsDeepLink;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
@@ -20,7 +21,7 @@ internal sealed class NewPlusModuleCommandProvider : ModuleCommandProvider
|
||||
yield return new ListItem(new OpenInSettingsCommand(SettingsWindow.NewPlus, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open New+ settings",
|
||||
Subtitle = Resources.NewPlus_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
using static Common.UI.SettingsDeepLink;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
@@ -20,7 +21,7 @@ internal sealed class PeekModuleCommandProvider : ModuleCommandProvider
|
||||
yield return new ListItem(new OpenInSettingsCommand(SettingsWindow.Peek, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open Peek settings",
|
||||
Subtitle = Resources.Peek_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
using static Common.UI.SettingsDeepLink;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
@@ -20,7 +21,7 @@ internal sealed class PowerRenameModuleCommandProvider : ModuleCommandProvider
|
||||
yield return new ListItem(new OpenInSettingsCommand(SettingsWindow.PowerRename, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open PowerRename settings",
|
||||
Subtitle = Resources.PowerRename_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
using static Common.UI.SettingsDeepLink;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
@@ -20,7 +21,7 @@ internal sealed class PowerToysRunModuleCommandProvider : ModuleCommandProvider
|
||||
yield return new ListItem(new OpenInSettingsCommand(SettingsWindow.PowerLauncher, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open PowerToys Run settings",
|
||||
Subtitle = Resources.PowerToysRun_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
using static Common.UI.SettingsDeepLink;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
@@ -20,7 +21,7 @@ internal sealed class QuickAccentModuleCommandProvider : ModuleCommandProvider
|
||||
yield return new ListItem(new OpenInSettingsCommand(SettingsWindow.PowerAccent, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open Quick Accent settings",
|
||||
Subtitle = Resources.QuickAccent_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
using static Common.UI.SettingsDeepLink;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
@@ -22,8 +23,8 @@ internal sealed class RegistryPreviewModuleCommandProvider : ModuleCommandProvid
|
||||
{
|
||||
yield return new ListItem(new OpenRegistryPreviewCommand())
|
||||
{
|
||||
Title = "Open Registry Preview",
|
||||
Subtitle = "Launch Registry Preview",
|
||||
Title = Resources.RegistryPreview_Open_Title,
|
||||
Subtitle = Resources.RegistryPreview_Open_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
@@ -31,7 +32,7 @@ internal sealed class RegistryPreviewModuleCommandProvider : ModuleCommandProvid
|
||||
yield return new ListItem(new OpenInSettingsCommand(module, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open Registry Preview settings",
|
||||
Subtitle = Resources.RegistryPreview_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
using static Common.UI.SettingsDeepLink;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
@@ -22,8 +23,8 @@ internal sealed class ScreenRulerModuleCommandProvider : ModuleCommandProvider
|
||||
{
|
||||
yield return new ListItem(new ToggleScreenRulerCommand())
|
||||
{
|
||||
Title = "Toggle Screen Ruler",
|
||||
Subtitle = "Start or close Screen Ruler",
|
||||
Title = Resources.ScreenRuler_Toggle_Title,
|
||||
Subtitle = Resources.ScreenRuler_Toggle_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
@@ -31,7 +32,7 @@ internal sealed class ScreenRulerModuleCommandProvider : ModuleCommandProvider
|
||||
yield return new ListItem(new OpenInSettingsCommand(module, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open Screen Ruler settings",
|
||||
Subtitle = Resources.ScreenRuler_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
using static Common.UI.SettingsDeepLink;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
@@ -22,8 +23,8 @@ internal sealed class ShortcutGuideModuleCommandProvider : ModuleCommandProvider
|
||||
{
|
||||
yield return new ListItem(new ToggleShortcutGuideCommand())
|
||||
{
|
||||
Title = "Toggle Shortcut Guide",
|
||||
Subtitle = "Show or hide Shortcut Guide",
|
||||
Title = Resources.ShortcutGuide_Toggle_Title,
|
||||
Subtitle = Resources.ShortcutGuide_Toggle_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
@@ -31,7 +32,7 @@ internal sealed class ShortcutGuideModuleCommandProvider : ModuleCommandProvider
|
||||
yield return new ListItem(new OpenInSettingsCommand(module, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open Shortcut Guide settings",
|
||||
Subtitle = Resources.ShortcutGuide_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
using static Common.UI.SettingsDeepLink;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
@@ -22,8 +23,8 @@ internal sealed class TextExtractorModuleCommandProvider : ModuleCommandProvider
|
||||
{
|
||||
yield return new ListItem(new ToggleTextExtractorCommand())
|
||||
{
|
||||
Title = "Toggle Text Extractor",
|
||||
Subtitle = "Start or close Text Extractor",
|
||||
Title = Resources.TextExtractor_Toggle_Title,
|
||||
Subtitle = Resources.TextExtractor_Toggle_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
@@ -31,7 +32,7 @@ internal sealed class TextExtractorModuleCommandProvider : ModuleCommandProvider
|
||||
yield return new ListItem(new OpenInSettingsCommand(module, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open Text Extractor settings",
|
||||
Subtitle = Resources.TextExtractor_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using Common.UI;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
using Workspaces.ModuleServices;
|
||||
using WorkspacesCsharpLibrary.Data;
|
||||
|
||||
@@ -25,7 +26,7 @@ internal sealed class WorkspacesModuleCommandProvider : ModuleCommandProvider
|
||||
items.Add(new ListItem(new OpenInSettingsCommand(module, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open Workspaces settings",
|
||||
Subtitle = Resources.Workspaces_Settings_Subtitle,
|
||||
Icon = moduleIcon,
|
||||
});
|
||||
|
||||
@@ -37,8 +38,8 @@ internal sealed class WorkspacesModuleCommandProvider : ModuleCommandProvider
|
||||
// Settings entry plus common actions.
|
||||
items.Add(new ListItem(new OpenWorkspaceEditorCommand())
|
||||
{
|
||||
Title = "Workspaces: Open editor",
|
||||
Subtitle = "Create or edit workspaces",
|
||||
Title = Resources.Workspaces_OpenEditor_Title,
|
||||
Subtitle = Resources.Workspaces_OpenEditor_Subtitle,
|
||||
Icon = icon,
|
||||
});
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
using static Common.UI.SettingsDeepLink;
|
||||
|
||||
namespace PowerToysExtension.Modules;
|
||||
@@ -21,40 +22,40 @@ internal sealed class ZoomItModuleCommandProvider : ModuleCommandProvider
|
||||
if (ModuleEnablementService.IsModuleEnabled(module))
|
||||
{
|
||||
// Action commands via ZoomIt IPC
|
||||
yield return new ListItem(new ZoomItActionCommand("zoom", "ZoomIt: Zoom"))
|
||||
yield return new ListItem(new ZoomItActionCommand("zoom", Resources.ZoomIt_Zoom_Title))
|
||||
{
|
||||
Title = "ZoomIt: Zoom",
|
||||
Subtitle = "Enter zoom mode",
|
||||
Title = Resources.ZoomIt_Zoom_Title,
|
||||
Subtitle = Resources.ZoomIt_Zoom_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
yield return new ListItem(new ZoomItActionCommand("draw", "ZoomIt: Draw"))
|
||||
yield return new ListItem(new ZoomItActionCommand("draw", Resources.ZoomIt_Draw_Title))
|
||||
{
|
||||
Title = "ZoomIt: Draw",
|
||||
Subtitle = "Enter drawing mode",
|
||||
Title = Resources.ZoomIt_Draw_Title,
|
||||
Subtitle = Resources.ZoomIt_Draw_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
yield return new ListItem(new ZoomItActionCommand("break", "ZoomIt: Break"))
|
||||
yield return new ListItem(new ZoomItActionCommand("break", Resources.ZoomIt_Break_Title))
|
||||
{
|
||||
Title = "ZoomIt: Break",
|
||||
Subtitle = "Enter break timer",
|
||||
Title = Resources.ZoomIt_Break_Title,
|
||||
Subtitle = Resources.ZoomIt_Break_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
yield return new ListItem(new ZoomItActionCommand("liveZoom", "ZoomIt: Live Zoom"))
|
||||
yield return new ListItem(new ZoomItActionCommand("liveZoom", Resources.ZoomIt_LiveZoom_Title))
|
||||
{
|
||||
Title = "ZoomIt: Live Zoom",
|
||||
Subtitle = "Toggle live zoom",
|
||||
Title = Resources.ZoomIt_LiveZoom_Title,
|
||||
Subtitle = Resources.ZoomIt_LiveZoom_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
yield return new ListItem(new ZoomItActionCommand("snip", "ZoomIt: Snip"))
|
||||
yield return new ListItem(new ZoomItActionCommand("snip", Resources.ZoomIt_Snip_Title))
|
||||
{
|
||||
Title = "ZoomIt: Snip",
|
||||
Subtitle = "Enter snip mode",
|
||||
Title = Resources.ZoomIt_Snip_Title,
|
||||
Subtitle = Resources.ZoomIt_Snip_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
yield return new ListItem(new ZoomItActionCommand("record", "ZoomIt: Record"))
|
||||
yield return new ListItem(new ZoomItActionCommand("record", Resources.ZoomIt_Record_Title))
|
||||
{
|
||||
Title = "ZoomIt: Record",
|
||||
Subtitle = "Start recording",
|
||||
Title = Resources.ZoomIt_Record_Title,
|
||||
Subtitle = Resources.ZoomIt_Record_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
@@ -62,7 +63,7 @@ internal sealed class ZoomItModuleCommandProvider : ModuleCommandProvider
|
||||
yield return new ListItem(new OpenInSettingsCommand(module, title))
|
||||
{
|
||||
Title = title,
|
||||
Subtitle = "Open ZoomIt settings",
|
||||
Subtitle = Resources.ZoomIt_Settings_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using ColorPicker.ModuleServices;
|
||||
@@ -10,24 +11,27 @@ using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
|
||||
namespace PowerToysExtension.Pages;
|
||||
|
||||
internal sealed partial class ColorPickerSavedColorsPage : DynamicListPage
|
||||
{
|
||||
private static readonly CompositeFormat NoMatchingSavedColorsFormat = CompositeFormat.Parse(Resources.ColorPicker_NoMatchingSavedColors_Subtitle);
|
||||
|
||||
private readonly CommandItem _emptyContent;
|
||||
|
||||
public ColorPickerSavedColorsPage()
|
||||
{
|
||||
Icon = PowerToysResourcesHelper.IconFromSettingsIcon("ColorPicker.png");
|
||||
Title = "Saved colors";
|
||||
Title = Resources.ColorPicker_SavedColors_Title;
|
||||
Name = "ColorPickerSavedColors";
|
||||
Id = "com.microsoft.powertoys.colorpicker.savedColors";
|
||||
|
||||
_emptyContent = new CommandItem()
|
||||
{
|
||||
Title = "No saved colors",
|
||||
Subtitle = "Pick a color first, then try again.",
|
||||
Title = Resources.ColorPicker_NoSavedColors_Title,
|
||||
Subtitle = Resources.ColorPicker_NoSavedColors_Subtitle,
|
||||
Icon = PowerToysResourcesHelper.IconFromSettingsIcon("ColorPicker.png"),
|
||||
};
|
||||
|
||||
@@ -70,8 +74,8 @@ internal sealed partial class ColorPickerSavedColorsPage : DynamicListPage
|
||||
public override void UpdateSearchText(string oldSearch, string newSearch)
|
||||
{
|
||||
_emptyContent.Subtitle = string.IsNullOrWhiteSpace(newSearch)
|
||||
? "Pick a color first, then try again."
|
||||
: $"No saved colors matching '{newSearch}'";
|
||||
? Resources.ColorPicker_NoSavedColors_Subtitle
|
||||
: string.Format(CultureInfo.CurrentCulture, NoMatchingSavedColorsFormat, newSearch);
|
||||
|
||||
RaiseItemsChanged(0);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
|
||||
namespace PowerToysExtension.Pages;
|
||||
|
||||
@@ -21,13 +22,13 @@ internal sealed partial class FancyZonesLayoutsPage : DynamicListPage
|
||||
public FancyZonesLayoutsPage()
|
||||
{
|
||||
Icon = PowerToysResourcesHelper.IconFromSettingsIcon("FancyZones.png");
|
||||
Name = Title = "FancyZones Layouts";
|
||||
Name = Title = Resources.FancyZones_Layouts_Title;
|
||||
Id = "com.microsoft.cmdpal.powertoys.fancyzones.layouts";
|
||||
|
||||
_emptyMessage = new CommandItem()
|
||||
{
|
||||
Title = "No layouts found",
|
||||
Subtitle = "Open FancyZones Editor once to initialize layouts.",
|
||||
Title = Resources.FancyZones_NoLayoutsFound_Title,
|
||||
Subtitle = Resources.FancyZones_NoLayoutsFound_Subtitle,
|
||||
Icon = PowerToysResourcesHelper.IconFromSettingsIcon("FancyZones.png"),
|
||||
};
|
||||
EmptyContent = _emptyMessage;
|
||||
|
||||
@@ -4,16 +4,21 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
|
||||
namespace PowerToysExtension.Pages;
|
||||
|
||||
internal sealed partial class FancyZonesMonitorLayoutPickerPage : DynamicListPage
|
||||
{
|
||||
private static readonly CompositeFormat SetActiveLayoutForFormat = CompositeFormat.Parse(Resources.FancyZones_SetActiveLayoutFor_Format);
|
||||
|
||||
private readonly FancyZonesMonitorDescriptor _monitor;
|
||||
private readonly CommandItem _emptyMessage;
|
||||
|
||||
@@ -21,13 +26,13 @@ internal sealed partial class FancyZonesMonitorLayoutPickerPage : DynamicListPag
|
||||
{
|
||||
_monitor = monitor;
|
||||
Icon = PowerToysResourcesHelper.IconFromSettingsIcon("FancyZones.png");
|
||||
Name = Title = $"Set active layout for {_monitor.Title}";
|
||||
Name = Title = string.Format(CultureInfo.CurrentCulture, SetActiveLayoutForFormat, _monitor.Title);
|
||||
Id = $"com.microsoft.cmdpal.powertoys.fancyzones.monitor.{_monitor.Index}.layouts";
|
||||
|
||||
_emptyMessage = new CommandItem()
|
||||
{
|
||||
Title = "No layouts found",
|
||||
Subtitle = "Open FancyZones Editor once to initialize layouts.",
|
||||
Title = Resources.FancyZones_NoLayoutsFound_Title,
|
||||
Subtitle = Resources.FancyZones_NoLayoutsFound_Subtitle,
|
||||
Icon = PowerToysResourcesHelper.IconFromSettingsIcon("FancyZones.png"),
|
||||
};
|
||||
EmptyContent = _emptyMessage;
|
||||
|
||||
@@ -4,26 +4,31 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
|
||||
namespace PowerToysExtension.Pages;
|
||||
|
||||
internal sealed partial class FancyZonesMonitorsPage : DynamicListPage
|
||||
{
|
||||
private static readonly CompositeFormat CurrentLayoutFormat = CompositeFormat.Parse(Resources.FancyZones_CurrentLayout_Format);
|
||||
|
||||
private readonly CommandItem _emptyMessage;
|
||||
|
||||
public FancyZonesMonitorsPage()
|
||||
{
|
||||
Icon = PowerToysResourcesHelper.IconFromSettingsIcon("FancyZones.png");
|
||||
Name = Title = "FancyZones Monitors";
|
||||
Name = Title = Resources.FancyZones_Monitors_Title;
|
||||
Id = "com.microsoft.cmdpal.powertoys.fancyzones.monitors";
|
||||
|
||||
_emptyMessage = new CommandItem()
|
||||
{
|
||||
Title = "No monitors found",
|
||||
Subtitle = "Open FancyZones Editor once to initialize monitor data.",
|
||||
Title = Resources.FancyZones_NoMonitorsFound_Title,
|
||||
Subtitle = Resources.FancyZones_NoMonitorsFound_Subtitle,
|
||||
Icon = PowerToysResourcesHelper.IconFromSettingsIcon("FancyZones.png"),
|
||||
};
|
||||
EmptyContent = _emptyMessage;
|
||||
@@ -55,8 +60,8 @@ internal sealed partial class FancyZonesMonitorsPage : DynamicListPage
|
||||
}
|
||||
|
||||
var layoutDescription = FancyZonesDataService.TryGetAppliedLayoutForMonitor(monitor.Data, out var applied) && applied is not null
|
||||
? $"Current layout: {applied.Value.Type}"
|
||||
: "Current layout: unknown";
|
||||
? string.Format(CultureInfo.CurrentCulture, CurrentLayoutFormat, applied.Value.Type)
|
||||
: Resources.FancyZones_CurrentLayout_Unknown;
|
||||
|
||||
var item = new FancyZonesMonitorListItem(monitor, layoutDescription, monitorIcon);
|
||||
items.Add(item);
|
||||
|
||||
@@ -6,6 +6,7 @@ using Awake.ModuleServices;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Commands;
|
||||
using PowerToysExtension.Properties;
|
||||
|
||||
namespace PowerToysExtension;
|
||||
|
||||
@@ -14,32 +15,32 @@ internal sealed partial class PowerToysExtensionPage : ListPage
|
||||
public PowerToysExtensionPage()
|
||||
{
|
||||
Icon = Helpers.PowerToysResourcesHelper.IconFromSettingsIcon("PowerToys.png");
|
||||
Title = "PowerToys";
|
||||
Name = "PowerToys commands";
|
||||
Title = Resources.PowerToys_DisplayName;
|
||||
Name = Resources.PowerToysExtension_CommandsName;
|
||||
}
|
||||
|
||||
public override IListItem[] GetItems()
|
||||
{
|
||||
return [
|
||||
new ListItem(new LaunchModuleCommand("PowerToys", executableName: "PowerToys.exe", displayName: "Open PowerToys"))
|
||||
new ListItem(new LaunchModuleCommand("PowerToys", executableName: "PowerToys.exe", displayName: Resources.PowerToysExtension_OpenPowerToys_Title))
|
||||
{
|
||||
Title = "Open PowerToys",
|
||||
Subtitle = "Launch the PowerToys shell",
|
||||
Title = Resources.PowerToysExtension_OpenPowerToys_Title,
|
||||
Subtitle = Resources.PowerToysExtension_OpenPowerToys_Subtitle,
|
||||
},
|
||||
new ListItem(new OpenPowerToysSettingsCommand("PowerToys", "General"))
|
||||
{
|
||||
Title = "Open PowerToys settings",
|
||||
Subtitle = "Open the main PowerToys settings window",
|
||||
Title = Resources.PowerToysExtension_OpenSettings_Title,
|
||||
Subtitle = Resources.PowerToysExtension_OpenSettings_Subtitle,
|
||||
},
|
||||
new ListItem(new OpenPowerToysSettingsCommand("Workspaces", "Workspaces"))
|
||||
{
|
||||
Title = "Open Workspaces settings",
|
||||
Subtitle = "Jump directly to Workspaces settings",
|
||||
Title = Resources.PowerToysExtension_OpenWorkspacesSettings_Title,
|
||||
Subtitle = Resources.PowerToysExtension_OpenWorkspacesSettings_Subtitle,
|
||||
},
|
||||
new ListItem(new OpenWorkspaceEditorCommand())
|
||||
{
|
||||
Title = "Open Workspaces editor",
|
||||
Subtitle = "Launch the Workspaces editor",
|
||||
Title = Resources.PowerToysExtension_OpenWorkspacesEditor_Title,
|
||||
Subtitle = Resources.PowerToysExtension_OpenWorkspacesEditor_Subtitle,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
|
||||
namespace PowerToysExtension.Pages;
|
||||
|
||||
@@ -15,13 +16,13 @@ internal sealed partial class PowerToysListPage : ListPage
|
||||
public PowerToysListPage()
|
||||
{
|
||||
Icon = PowerToysResourcesHelper.IconFromSettingsIcon("PowerToys.png");
|
||||
Name = Title = "PowerToys";
|
||||
Name = Title = Resources.PowerToys_DisplayName;
|
||||
Id = "com.microsoft.cmdpal.powertoys";
|
||||
SettingsChangeNotifier.SettingsChanged += OnSettingsChanged;
|
||||
_empty = new CommandItem()
|
||||
{
|
||||
Icon = PowerToysResourcesHelper.IconFromSettingsIcon("PowerToys.png"),
|
||||
Title = "No matching module found",
|
||||
Title = Resources.PowerToys_NoMatchingModule,
|
||||
Subtitle = SearchText,
|
||||
};
|
||||
EmptyContent = _empty;
|
||||
|
||||
@@ -7,6 +7,7 @@ using ManagedCommon;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
|
||||
namespace PowerToysExtension;
|
||||
|
||||
@@ -14,7 +15,7 @@ public sealed partial class PowerToysCommandsProvider : CommandProvider
|
||||
{
|
||||
public PowerToysCommandsProvider()
|
||||
{
|
||||
DisplayName = "PowerToys";
|
||||
DisplayName = Resources.PowerToys_DisplayName;
|
||||
Icon = PowerToysResourcesHelper.IconFromSettingsIcon("PowerToys.png");
|
||||
}
|
||||
|
||||
@@ -22,8 +23,8 @@ public sealed partial class PowerToysCommandsProvider : CommandProvider
|
||||
[
|
||||
new CommandItem(new Pages.PowerToysListPage())
|
||||
{
|
||||
Title = "PowerToys",
|
||||
Subtitle = "PowerToys commands and settings",
|
||||
Title = Resources.PowerToys_DisplayName,
|
||||
Subtitle = Resources.PowerToys_Subtitle,
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToysExtension.Helpers;
|
||||
using PowerToysExtension.Properties;
|
||||
|
||||
namespace PowerToysExtension;
|
||||
|
||||
@@ -15,13 +16,13 @@ public partial class PowerToysExtensionCommandsProvider : CommandProvider
|
||||
|
||||
public PowerToysExtensionCommandsProvider()
|
||||
{
|
||||
DisplayName = "PowerToys";
|
||||
DisplayName = Resources.PowerToys_DisplayName;
|
||||
Icon = PowerToysResourcesHelper.IconFromSettingsIcon("PowerToys.png");
|
||||
_commands = [
|
||||
new CommandItem(new Pages.PowerToysListPage())
|
||||
{
|
||||
Title = "PowerToys",
|
||||
Subtitle = "PowerToys commands and settings",
|
||||
Title = Resources.PowerToys_DisplayName,
|
||||
Subtitle = Resources.PowerToys_Subtitle,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
1503
src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Properties/Resources.Designer.cs
generated
Normal file
1503
src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PowerToys/Properties/Resources.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,630 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<!-- PowerToys CommandsProvider -->
|
||||
<data name="PowerToys_DisplayName" xml:space="preserve">
|
||||
<value>PowerToys</value>
|
||||
</data>
|
||||
<data name="PowerToys_Subtitle" xml:space="preserve">
|
||||
<value>PowerToys commands and settings</value>
|
||||
</data>
|
||||
<data name="PowerToys_NoMatchingModule" xml:space="preserve">
|
||||
<value>No matching module found</value>
|
||||
</data>
|
||||
<!-- PowerToys Extension Page -->
|
||||
<data name="PowerToysExtension_CommandsName" xml:space="preserve">
|
||||
<value>PowerToys commands</value>
|
||||
</data>
|
||||
<data name="PowerToysExtension_OpenPowerToys_Title" xml:space="preserve">
|
||||
<value>Open PowerToys</value>
|
||||
</data>
|
||||
<data name="PowerToysExtension_OpenPowerToys_Subtitle" xml:space="preserve">
|
||||
<value>Launch the PowerToys shell</value>
|
||||
</data>
|
||||
<data name="PowerToysExtension_OpenSettings_Title" xml:space="preserve">
|
||||
<value>Open PowerToys settings</value>
|
||||
</data>
|
||||
<data name="PowerToysExtension_OpenSettings_Subtitle" xml:space="preserve">
|
||||
<value>Open the main PowerToys settings window</value>
|
||||
</data>
|
||||
<data name="PowerToysExtension_OpenWorkspacesSettings_Title" xml:space="preserve">
|
||||
<value>Open Workspaces settings</value>
|
||||
</data>
|
||||
<data name="PowerToysExtension_OpenWorkspacesSettings_Subtitle" xml:space="preserve">
|
||||
<value>Jump directly to Workspaces settings</value>
|
||||
</data>
|
||||
<data name="PowerToysExtension_OpenWorkspacesEditor_Title" xml:space="preserve">
|
||||
<value>Open Workspaces editor</value>
|
||||
</data>
|
||||
<data name="PowerToysExtension_OpenWorkspacesEditor_Subtitle" xml:space="preserve">
|
||||
<value>Launch the Workspaces editor</value>
|
||||
</data>
|
||||
<!-- Advanced Paste Module -->
|
||||
<data name="AdvancedPaste_Open_Title" xml:space="preserve">
|
||||
<value>Open Advanced Paste</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_Open_Subtitle" xml:space="preserve">
|
||||
<value>Launch the Advanced Paste UI</value>
|
||||
</data>
|
||||
<data name="AdvancedPaste_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open Advanced Paste settings</value>
|
||||
</data>
|
||||
<!-- Always On Top Module -->
|
||||
<data name="AlwaysOnTop_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open Always On Top settings</value>
|
||||
</data>
|
||||
<!-- Awake Module -->
|
||||
<data name="Awake_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open Awake settings</value>
|
||||
</data>
|
||||
<data name="Awake_Status_Title" xml:space="preserve">
|
||||
<value>Awake: Current status</value>
|
||||
</data>
|
||||
<data name="Awake_KeepIndefinite_Title" xml:space="preserve">
|
||||
<value>Awake: Keep awake indefinitely</value>
|
||||
</data>
|
||||
<data name="Awake_KeepIndefinite_Subtitle" xml:space="preserve">
|
||||
<value>Run Awake in indefinite mode</value>
|
||||
</data>
|
||||
<data name="Awake_Keep30Min_Title" xml:space="preserve">
|
||||
<value>Awake: Keep awake for 30 minutes</value>
|
||||
</data>
|
||||
<data name="Awake_Keep30Min_Subtitle" xml:space="preserve">
|
||||
<value>Run Awake timed for 30 minutes</value>
|
||||
</data>
|
||||
<data name="Awake_Keep1Hour_Title" xml:space="preserve">
|
||||
<value>Awake: Keep awake for 1 hour</value>
|
||||
</data>
|
||||
<data name="Awake_Keep1Hour_Subtitle" xml:space="preserve">
|
||||
<value>Run Awake timed for 1 hour</value>
|
||||
</data>
|
||||
<data name="Awake_Keep2Hours_Title" xml:space="preserve">
|
||||
<value>Awake: Keep awake for 2 hours</value>
|
||||
</data>
|
||||
<data name="Awake_Keep2Hours_Subtitle" xml:space="preserve">
|
||||
<value>Run Awake timed for 2 hours</value>
|
||||
</data>
|
||||
<data name="Awake_TurnOff_Title" xml:space="preserve">
|
||||
<value>Awake: Turn off</value>
|
||||
</data>
|
||||
<data name="Awake_TurnOff_Subtitle" xml:space="preserve">
|
||||
<value>Switch Awake back to Off</value>
|
||||
</data>
|
||||
<data name="Awake_SetIndefinite_Toast" xml:space="preserve">
|
||||
<value>Awake set to indefinite</value>
|
||||
</data>
|
||||
<data name="Awake_Set30Min_Toast" xml:space="preserve">
|
||||
<value>Awake set for 30 minutes</value>
|
||||
</data>
|
||||
<data name="Awake_Set1Hour_Toast" xml:space="preserve">
|
||||
<value>Awake set for 1 hour</value>
|
||||
</data>
|
||||
<data name="Awake_Set2Hours_Toast" xml:space="preserve">
|
||||
<value>Awake set for 2 hours</value>
|
||||
</data>
|
||||
<!-- Color Picker Module -->
|
||||
<data name="ColorPicker_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open Color Picker settings</value>
|
||||
</data>
|
||||
<data name="ColorPicker_Open_Title" xml:space="preserve">
|
||||
<value>Open Color Picker</value>
|
||||
</data>
|
||||
<data name="ColorPicker_Open_Subtitle" xml:space="preserve">
|
||||
<value>Start a color pick session</value>
|
||||
</data>
|
||||
<data name="ColorPicker_SavedColors_Title" xml:space="preserve">
|
||||
<value>Saved colors</value>
|
||||
</data>
|
||||
<data name="ColorPicker_SavedColors_Subtitle" xml:space="preserve">
|
||||
<value>Browse and copy saved colors</value>
|
||||
</data>
|
||||
<data name="ColorPicker_NoSavedColors_Title" xml:space="preserve">
|
||||
<value>No saved colors</value>
|
||||
</data>
|
||||
<data name="ColorPicker_NoSavedColors_Subtitle" xml:space="preserve">
|
||||
<value>Pick a color first, then try again.</value>
|
||||
</data>
|
||||
<data name="ColorPicker_NoMatchingSavedColors_Subtitle" xml:space="preserve">
|
||||
<value>No saved colors matching '{0}'</value>
|
||||
</data>
|
||||
<!-- Command Not Found Module -->
|
||||
<data name="CommandNotFound_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open Command Not Found settings</value>
|
||||
</data>
|
||||
<!-- Crop And Lock Module -->
|
||||
<data name="CropAndLock_Reparent_Title" xml:space="preserve">
|
||||
<value>Crop and Lock (Reparent)</value>
|
||||
</data>
|
||||
<data name="CropAndLock_Reparent_Subtitle" xml:space="preserve">
|
||||
<value>Create a cropped reparented window</value>
|
||||
</data>
|
||||
<data name="CropAndLock_Thumbnail_Title" xml:space="preserve">
|
||||
<value>Crop and Lock (Thumbnail)</value>
|
||||
</data>
|
||||
<data name="CropAndLock_Thumbnail_Subtitle" xml:space="preserve">
|
||||
<value>Create a cropped thumbnail window</value>
|
||||
</data>
|
||||
<data name="CropAndLock_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open Crop and Lock settings</value>
|
||||
</data>
|
||||
<!-- Environment Variables Module -->
|
||||
<data name="EnvironmentVariables_Open_Title" xml:space="preserve">
|
||||
<value>Open Environment Variables</value>
|
||||
</data>
|
||||
<data name="EnvironmentVariables_Open_Subtitle" xml:space="preserve">
|
||||
<value>Launch Environment Variables editor</value>
|
||||
</data>
|
||||
<data name="EnvironmentVariables_OpenAdmin_Title" xml:space="preserve">
|
||||
<value>Open Environment Variables (Admin)</value>
|
||||
</data>
|
||||
<data name="EnvironmentVariables_OpenAdmin_Subtitle" xml:space="preserve">
|
||||
<value>Launch Environment Variables editor as admin</value>
|
||||
</data>
|
||||
<data name="EnvironmentVariables_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open Environment Variables settings</value>
|
||||
</data>
|
||||
<!-- FancyZones Module -->
|
||||
<data name="FancyZones_Layouts_Title" xml:space="preserve">
|
||||
<value>FancyZones: Layouts</value>
|
||||
</data>
|
||||
<data name="FancyZones_Layouts_Subtitle" xml:space="preserve">
|
||||
<value>Apply a layout to all monitors or a specific monitor</value>
|
||||
</data>
|
||||
<data name="FancyZones_Monitors_Title" xml:space="preserve">
|
||||
<value>FancyZones: Monitors</value>
|
||||
</data>
|
||||
<data name="FancyZones_Monitors_Subtitle" xml:space="preserve">
|
||||
<value>Identify monitors and apply layouts</value>
|
||||
</data>
|
||||
<data name="FancyZones_OpenEditor_Title" xml:space="preserve">
|
||||
<value>Open FancyZones Editor</value>
|
||||
</data>
|
||||
<data name="FancyZones_OpenEditor_Subtitle" xml:space="preserve">
|
||||
<value>Launch layout editor</value>
|
||||
</data>
|
||||
<data name="FancyZones_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open FancyZones settings</value>
|
||||
</data>
|
||||
<data name="FancyZones_LayoutsPage_Title" xml:space="preserve">
|
||||
<value>FancyZones Layouts</value>
|
||||
</data>
|
||||
<data name="FancyZones_NoLayoutsFound_Title" xml:space="preserve">
|
||||
<value>No layouts found</value>
|
||||
</data>
|
||||
<data name="FancyZones_NoLayoutsFound_Subtitle" xml:space="preserve">
|
||||
<value>Open FancyZones Editor once to initialize layouts.</value>
|
||||
</data>
|
||||
<data name="FancyZones_ApplyTo_Format" xml:space="preserve">
|
||||
<value>Apply to {0}</value>
|
||||
</data>
|
||||
<data name="FancyZones_MonitorsPage_Title" xml:space="preserve">
|
||||
<value>FancyZones Monitors</value>
|
||||
</data>
|
||||
<data name="FancyZones_NoMonitorsFound_Title" xml:space="preserve">
|
||||
<value>No monitors found</value>
|
||||
</data>
|
||||
<data name="FancyZones_NoMonitorsFound_Subtitle" xml:space="preserve">
|
||||
<value>Open FancyZones Editor once to initialize monitor data.</value>
|
||||
</data>
|
||||
<data name="FancyZones_SetActiveLayout" xml:space="preserve">
|
||||
<value>Set active layout</value>
|
||||
</data>
|
||||
<data name="FancyZones_PickLayoutForMonitor" xml:space="preserve">
|
||||
<value>Pick a layout for this monitor</value>
|
||||
</data>
|
||||
<data name="FancyZones_SetActiveLayoutFor_Format" xml:space="preserve">
|
||||
<value>Set active layout for {0}</value>
|
||||
</data>
|
||||
<data name="FancyZones_CurrentLayout_Format" xml:space="preserve">
|
||||
<value>Current layout: {0}</value>
|
||||
</data>
|
||||
<data name="FancyZones_CurrentLayout_Unknown" xml:space="preserve">
|
||||
<value>Current layout: unknown</value>
|
||||
</data>
|
||||
<data name="FancyZones_Template_Format" xml:space="preserve">
|
||||
<value>Template: {0}</value>
|
||||
</data>
|
||||
<data name="FancyZones_Zones_Format" xml:space="preserve">
|
||||
<value>{0} zones</value>
|
||||
</data>
|
||||
<data name="FancyZones_CustomGrid_Zones_Format" xml:space="preserve">
|
||||
<value>Custom grid • {0} zones</value>
|
||||
</data>
|
||||
<data name="FancyZones_CustomCanvas_Zones_Format" xml:space="preserve">
|
||||
<value>Custom canvas • {0} zones</value>
|
||||
</data>
|
||||
<data name="FancyZones_Custom_Zones_Format" xml:space="preserve">
|
||||
<value>Custom • {0} zones</value>
|
||||
</data>
|
||||
<data name="FancyZones_LayoutApplied" xml:space="preserve">
|
||||
<value>Layout applied.</value>
|
||||
</data>
|
||||
<data name="FancyZones_LayoutAppliedNotifyFailed_Format" xml:space="preserve">
|
||||
<value>Layout applied, but FancyZones could not be notified: {0}</value>
|
||||
</data>
|
||||
<data name="FancyZones_WriteAppliedLayoutsFailed_Format" xml:space="preserve">
|
||||
<value>Failed to write applied layouts: {0}</value>
|
||||
</data>
|
||||
<data name="FancyZones_MonitorDataNotFound" xml:space="preserve">
|
||||
<value>FancyZones monitor data not found. Open FancyZones Editor once to initialize.</value>
|
||||
</data>
|
||||
<data name="FancyZones_NoFancyZonesMonitorsFound" xml:space="preserve">
|
||||
<value>No FancyZones monitors found.</value>
|
||||
</data>
|
||||
<data name="FancyZones_ReadMonitorDataFailed_Format" xml:space="preserve">
|
||||
<value>Failed to read FancyZones monitor data: {0}</value>
|
||||
</data>
|
||||
<!-- File Explorer Addons Module -->
|
||||
<data name="FileExplorerAddons_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open File Explorer add-ons settings</value>
|
||||
</data>
|
||||
<!-- File Locksmith Module -->
|
||||
<data name="FileLocksmith_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open File Locksmith settings</value>
|
||||
</data>
|
||||
<!-- Hosts Module -->
|
||||
<data name="Hosts_Open_Title" xml:space="preserve">
|
||||
<value>Open Hosts File Editor</value>
|
||||
</data>
|
||||
<data name="Hosts_Open_Subtitle" xml:space="preserve">
|
||||
<value>Launch Hosts File Editor</value>
|
||||
</data>
|
||||
<data name="Hosts_OpenAdmin_Title" xml:space="preserve">
|
||||
<value>Open Hosts File Editor (Admin)</value>
|
||||
</data>
|
||||
<data name="Hosts_OpenAdmin_Subtitle" xml:space="preserve">
|
||||
<value>Launch Hosts File Editor as admin</value>
|
||||
</data>
|
||||
<data name="Hosts_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open Hosts File Editor settings</value>
|
||||
</data>
|
||||
<!-- Image Resizer Module -->
|
||||
<data name="ImageResizer_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open Image Resizer settings</value>
|
||||
</data>
|
||||
<!-- Keyboard Manager Module -->
|
||||
<data name="KeyboardManager_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open Keyboard Manager settings</value>
|
||||
</data>
|
||||
<!-- Light Switch Module -->
|
||||
<data name="LightSwitch_Toggle_Title" xml:space="preserve">
|
||||
<value>Light Switch: Toggle theme</value>
|
||||
</data>
|
||||
<data name="LightSwitch_Toggle_Subtitle" xml:space="preserve">
|
||||
<value>Toggle system/apps theme immediately</value>
|
||||
</data>
|
||||
<data name="LightSwitch_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open Light Switch settings</value>
|
||||
</data>
|
||||
<!-- Mouse Utils Module -->
|
||||
<data name="MouseUtils_FindMyMouse_Title" xml:space="preserve">
|
||||
<value>Trigger Find My Mouse</value>
|
||||
</data>
|
||||
<data name="MouseUtils_FindMyMouse_Subtitle" xml:space="preserve">
|
||||
<value>Focus the mouse pointer</value>
|
||||
</data>
|
||||
<data name="MouseUtils_Highlighter_Title" xml:space="preserve">
|
||||
<value>Toggle Mouse Highlighter</value>
|
||||
</data>
|
||||
<data name="MouseUtils_Highlighter_Subtitle" xml:space="preserve">
|
||||
<value>Highlight mouse clicks</value>
|
||||
</data>
|
||||
<data name="MouseUtils_Crosshairs_Title" xml:space="preserve">
|
||||
<value>Toggle Mouse Crosshairs</value>
|
||||
</data>
|
||||
<data name="MouseUtils_Crosshairs_Subtitle" xml:space="preserve">
|
||||
<value>Enable or disable pointer crosshairs</value>
|
||||
</data>
|
||||
<data name="MouseUtils_CursorWrap_Title" xml:space="preserve">
|
||||
<value>Toggle Cursor Wrap</value>
|
||||
</data>
|
||||
<data name="MouseUtils_CursorWrap_Subtitle" xml:space="preserve">
|
||||
<value>Wrap the cursor across monitor edges</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_Title" xml:space="preserve">
|
||||
<value>Show Mouse Jump Preview</value>
|
||||
</data>
|
||||
<data name="MouseUtils_MouseJump_Subtitle" xml:space="preserve">
|
||||
<value>Jump the pointer to a target</value>
|
||||
</data>
|
||||
<data name="MouseUtils_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open Mouse Utilities settings</value>
|
||||
</data>
|
||||
<!-- Mouse Without Borders Module -->
|
||||
<data name="MouseWithoutBorders_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open Mouse Without Borders settings</value>
|
||||
</data>
|
||||
<!-- New+ Module -->
|
||||
<data name="NewPlus_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open New+ settings</value>
|
||||
</data>
|
||||
<!-- Peek Module -->
|
||||
<data name="Peek_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open Peek settings</value>
|
||||
</data>
|
||||
<!-- PowerRename Module -->
|
||||
<data name="PowerRename_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open PowerRename settings</value>
|
||||
</data>
|
||||
<!-- PowerToys Run Module -->
|
||||
<data name="PowerToysRun_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open PowerToys Run settings</value>
|
||||
</data>
|
||||
<!-- Quick Accent Module -->
|
||||
<data name="QuickAccent_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open Quick Accent settings</value>
|
||||
</data>
|
||||
<!-- Registry Preview Module -->
|
||||
<data name="RegistryPreview_Open_Title" xml:space="preserve">
|
||||
<value>Open Registry Preview</value>
|
||||
</data>
|
||||
<data name="RegistryPreview_Open_Subtitle" xml:space="preserve">
|
||||
<value>Launch Registry Preview</value>
|
||||
</data>
|
||||
<data name="RegistryPreview_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open Registry Preview settings</value>
|
||||
</data>
|
||||
<!-- Screen Ruler Module -->
|
||||
<data name="ScreenRuler_Toggle_Title" xml:space="preserve">
|
||||
<value>Toggle Screen Ruler</value>
|
||||
</data>
|
||||
<data name="ScreenRuler_Toggle_Subtitle" xml:space="preserve">
|
||||
<value>Start or close Screen Ruler</value>
|
||||
</data>
|
||||
<data name="ScreenRuler_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open Screen Ruler settings</value>
|
||||
</data>
|
||||
<!-- Shortcut Guide Module -->
|
||||
<data name="ShortcutGuide_Toggle_Title" xml:space="preserve">
|
||||
<value>Toggle Shortcut Guide</value>
|
||||
</data>
|
||||
<data name="ShortcutGuide_Toggle_Subtitle" xml:space="preserve">
|
||||
<value>Show or hide Shortcut Guide</value>
|
||||
</data>
|
||||
<data name="ShortcutGuide_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open Shortcut Guide settings</value>
|
||||
</data>
|
||||
<!-- Text Extractor Module -->
|
||||
<data name="TextExtractor_Toggle_Title" xml:space="preserve">
|
||||
<value>Toggle Text Extractor</value>
|
||||
</data>
|
||||
<data name="TextExtractor_Toggle_Subtitle" xml:space="preserve">
|
||||
<value>Start or close Text Extractor</value>
|
||||
</data>
|
||||
<data name="TextExtractor_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open Text Extractor settings</value>
|
||||
</data>
|
||||
<!-- Workspaces Module -->
|
||||
<data name="Workspaces_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open Workspaces settings</value>
|
||||
</data>
|
||||
<data name="Workspaces_OpenEditor_Title" xml:space="preserve">
|
||||
<value>Workspaces: Open editor</value>
|
||||
</data>
|
||||
<data name="Workspaces_OpenEditor_Subtitle" xml:space="preserve">
|
||||
<value>Create or edit workspaces</value>
|
||||
</data>
|
||||
<data name="Workspaces_NoApplications" xml:space="preserve">
|
||||
<value>No applications</value>
|
||||
</data>
|
||||
<data name="Workspaces_Applications_Format" xml:space="preserve">
|
||||
<value>{0} applications</value>
|
||||
</data>
|
||||
<data name="Workspaces_LastLaunched_Format" xml:space="preserve">
|
||||
<value>Last launched {0}</value>
|
||||
</data>
|
||||
<data name="Workspaces_NeverLaunched" xml:space="preserve">
|
||||
<value>Never launched</value>
|
||||
</data>
|
||||
<data name="Workspaces_NoApplicationsInWorkspace" xml:space="preserve">
|
||||
<value>No applications in this workspace</value>
|
||||
</data>
|
||||
<data name="Workspaces_OneApplication" xml:space="preserve">
|
||||
<value>1 application</value>
|
||||
</data>
|
||||
<data name="Workspaces_ApplicationsCount_Format" xml:space="preserve">
|
||||
<value>{0} applications</value>
|
||||
</data>
|
||||
<data name="Workspaces_Workspace" xml:space="preserve">
|
||||
<value>Workspace</value>
|
||||
</data>
|
||||
<data name="Workspaces_App" xml:space="preserve">
|
||||
<value>App</value>
|
||||
</data>
|
||||
<data name="Workspaces_JustNow" xml:space="preserve">
|
||||
<value>just now</value>
|
||||
</data>
|
||||
<data name="Workspaces_MinAgo_Format" xml:space="preserve">
|
||||
<value>{0} min ago</value>
|
||||
</data>
|
||||
<data name="Workspaces_HrAgo_Format" xml:space="preserve">
|
||||
<value>{0} hr ago</value>
|
||||
</data>
|
||||
<data name="Workspaces_DaysAgo_Format" xml:space="preserve">
|
||||
<value>{0} days ago</value>
|
||||
</data>
|
||||
<!-- ZoomIt Module -->
|
||||
<data name="ZoomIt_Zoom_Title" xml:space="preserve">
|
||||
<value>ZoomIt: Zoom</value>
|
||||
</data>
|
||||
<data name="ZoomIt_Zoom_Subtitle" xml:space="preserve">
|
||||
<value>Enter zoom mode</value>
|
||||
</data>
|
||||
<data name="ZoomIt_Draw_Title" xml:space="preserve">
|
||||
<value>ZoomIt: Draw</value>
|
||||
</data>
|
||||
<data name="ZoomIt_Draw_Subtitle" xml:space="preserve">
|
||||
<value>Enter drawing mode</value>
|
||||
</data>
|
||||
<data name="ZoomIt_Break_Title" xml:space="preserve">
|
||||
<value>ZoomIt: Break</value>
|
||||
</data>
|
||||
<data name="ZoomIt_Break_Subtitle" xml:space="preserve">
|
||||
<value>Enter break timer</value>
|
||||
</data>
|
||||
<data name="ZoomIt_LiveZoom_Title" xml:space="preserve">
|
||||
<value>ZoomIt: Live Zoom</value>
|
||||
</data>
|
||||
<data name="ZoomIt_LiveZoom_Subtitle" xml:space="preserve">
|
||||
<value>Toggle live zoom</value>
|
||||
</data>
|
||||
<data name="ZoomIt_Snip_Title" xml:space="preserve">
|
||||
<value>ZoomIt: Snip</value>
|
||||
</data>
|
||||
<data name="ZoomIt_Snip_Subtitle" xml:space="preserve">
|
||||
<value>Enter snip mode</value>
|
||||
</data>
|
||||
<data name="ZoomIt_Record_Title" xml:space="preserve">
|
||||
<value>ZoomIt: Record</value>
|
||||
</data>
|
||||
<data name="ZoomIt_Record_Subtitle" xml:space="preserve">
|
||||
<value>Start recording</value>
|
||||
</data>
|
||||
<data name="ZoomIt_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open ZoomIt settings</value>
|
||||
</data>
|
||||
<!-- FancyZones Monitor Details -->
|
||||
<data name="FancyZones_Monitor" xml:space="preserve">
|
||||
<value>Monitor</value>
|
||||
</data>
|
||||
<data name="FancyZones_Instance" xml:space="preserve">
|
||||
<value>Instance</value>
|
||||
</data>
|
||||
<data name="FancyZones_Serial" xml:space="preserve">
|
||||
<value>Serial</value>
|
||||
</data>
|
||||
<data name="FancyZones_Number" xml:space="preserve">
|
||||
<value>Number</value>
|
||||
</data>
|
||||
<data name="FancyZones_VirtualDesktop" xml:space="preserve">
|
||||
<value>Virtual desktop</value>
|
||||
</data>
|
||||
<data name="FancyZones_WorkArea" xml:space="preserve">
|
||||
<value>Work area</value>
|
||||
</data>
|
||||
<data name="FancyZones_Resolution" xml:space="preserve">
|
||||
<value>Resolution</value>
|
||||
</data>
|
||||
<data name="FancyZones_DPI" xml:space="preserve">
|
||||
<value>DPI</value>
|
||||
</data>
|
||||
<data name="Common_NotAvailable" xml:space="preserve">
|
||||
<value>N/A</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -7,7 +7,7 @@ Palette, and use the "Create a new extension" command. That will set up a
|
||||
project for you, with the packaging, dependencies, and basic program structure
|
||||
ready to go.
|
||||
|
||||
To view the full docs, you can head over to [our docs site](https://go.microsoft.com/fwlink/?linkid=2310639)
|
||||
To view the full docs, you can head over to [our docs site](https://aka.ms/cmdpalextensions-devdocs)
|
||||
|
||||
There are samples of just about everything you can do in [the samples project].
|
||||
Head over there to see basic usage of the APIs.
|
||||
|
||||
@@ -8,6 +8,8 @@ using System.CommandLine.Invocation;
|
||||
|
||||
using FancyZonesCLI;
|
||||
using FancyZonesCLI.CommandLine;
|
||||
using FancyZonesCLI.Telemetry;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
|
||||
namespace FancyZonesCLI.CommandLine.Commands;
|
||||
|
||||
@@ -24,12 +26,14 @@ internal abstract class FancyZonesBaseCommand : Command
|
||||
private void InvokeInternal(InvocationContext context)
|
||||
{
|
||||
Logger.LogInfo($"Executing command '{Name}'");
|
||||
bool successful = false;
|
||||
|
||||
if (!FancyZonesCliGuards.IsFancyZonesRunning())
|
||||
{
|
||||
Logger.LogWarning($"Command '{Name}' blocked: FancyZones is not running");
|
||||
context.Console.Error.Write($"Error: FancyZones is not running. Start PowerToys (FancyZones) and retry.{Environment.NewLine}");
|
||||
context.Console.Error.Write($"{Properties.Resources.error_fancyzones_not_running}{Environment.NewLine}");
|
||||
context.ExitCode = 1;
|
||||
LogTelemetry(successful: false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -37,6 +41,7 @@ internal abstract class FancyZonesBaseCommand : Command
|
||||
{
|
||||
string output = Execute(context);
|
||||
context.ExitCode = 0;
|
||||
successful = true;
|
||||
|
||||
Logger.LogInfo($"Command '{Name}' completed successfully");
|
||||
Logger.LogDebug($"Command '{Name}' output length: {output?.Length ?? 0}");
|
||||
@@ -52,6 +57,28 @@ internal abstract class FancyZonesBaseCommand : Command
|
||||
Logger.LogError($"Command '{Name}' failed", ex);
|
||||
context.Console.Error.Write($"Error: {ex.Message}{Environment.NewLine}");
|
||||
context.ExitCode = 1;
|
||||
successful = false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
LogTelemetry(successful);
|
||||
}
|
||||
}
|
||||
|
||||
private void LogTelemetry(bool successful)
|
||||
{
|
||||
try
|
||||
{
|
||||
PowerToysTelemetry.Log.WriteEvent(new FancyZonesCLICommandEvent
|
||||
{
|
||||
CommandName = Name,
|
||||
Successful = successful,
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Don't fail the command if telemetry logging fails
|
||||
Logger.LogError($"Failed to log telemetry for command '{Name}'", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace FancyZonesCLI.CommandLine.Commands;
|
||||
internal sealed partial class GetActiveLayoutCommand : FancyZonesBaseCommand
|
||||
{
|
||||
public GetActiveLayoutCommand()
|
||||
: base("get-active-layout", "Show currently active layout")
|
||||
: base("get-active-layout", Properties.Resources.cmd_get_active_layout)
|
||||
{
|
||||
AddAlias("active");
|
||||
}
|
||||
@@ -28,7 +28,7 @@ internal sealed partial class GetActiveLayoutCommand : FancyZonesBaseCommand
|
||||
|
||||
if (editorParams.Monitors == null || editorParams.Monitors.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("Could not get current monitor information.");
|
||||
throw new InvalidOperationException(Properties.Resources.get_active_layout_no_monitor_info);
|
||||
}
|
||||
|
||||
// Read applied layouts.
|
||||
@@ -36,11 +36,11 @@ internal sealed partial class GetActiveLayoutCommand : FancyZonesBaseCommand
|
||||
|
||||
if (appliedLayouts.AppliedLayouts == null)
|
||||
{
|
||||
return "No layouts configured.";
|
||||
return Properties.Resources.get_active_layout_no_layouts;
|
||||
}
|
||||
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.AppendLine("\n=== Active FancyZones Layout(s) ===\n");
|
||||
sb.AppendLine($"\n{Properties.Resources.get_active_layout_header}\n");
|
||||
|
||||
// Show only layouts for currently connected monitors.
|
||||
for (int i = 0; i < editorParams.Monitors.Count; i++)
|
||||
@@ -71,7 +71,7 @@ internal sealed partial class GetActiveLayoutCommand : FancyZonesBaseCommand
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine(" No layout applied");
|
||||
sb.AppendLine(Properties.Resources.get_active_layout_no_layout);
|
||||
}
|
||||
|
||||
if (i < editorParams.Monitors.Count - 1)
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace FancyZonesCLI.CommandLine.Commands;
|
||||
internal sealed partial class GetHotkeysCommand : FancyZonesBaseCommand
|
||||
{
|
||||
public GetHotkeysCommand()
|
||||
: base("get-hotkeys", "List all layout hotkeys")
|
||||
: base("get-hotkeys", Properties.Resources.cmd_get_hotkeys)
|
||||
{
|
||||
AddAlias("hk");
|
||||
}
|
||||
@@ -26,12 +26,12 @@ internal sealed partial class GetHotkeysCommand : FancyZonesBaseCommand
|
||||
|
||||
if (hotkeys.LayoutHotkeys == null || hotkeys.LayoutHotkeys.Count == 0)
|
||||
{
|
||||
return "No hotkeys configured.";
|
||||
return Properties.Resources.get_hotkeys_no_hotkeys;
|
||||
}
|
||||
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.AppendLine("=== Layout Hotkeys ===\n");
|
||||
sb.AppendLine("Press Win + Ctrl + Alt + <number> to switch layouts:\n");
|
||||
sb.AppendLine($"{Properties.Resources.get_hotkeys_header}\n");
|
||||
sb.AppendLine($"{Properties.Resources.get_hotkeys_instruction}\n");
|
||||
|
||||
foreach (var hotkey in hotkeys.LayoutHotkeys.OrderBy(h => h.Key))
|
||||
{
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace FancyZonesCLI.CommandLine.Commands;
|
||||
internal sealed partial class GetLayoutsCommand : FancyZonesBaseCommand
|
||||
{
|
||||
public GetLayoutsCommand()
|
||||
: base("get-layouts", "List available layouts")
|
||||
: base("get-layouts", Properties.Resources.cmd_get_layouts)
|
||||
{
|
||||
AddAlias("ls");
|
||||
}
|
||||
@@ -61,7 +61,7 @@ internal sealed partial class GetLayoutsCommand : FancyZonesBaseCommand
|
||||
|
||||
if (customLayouts.CustomLayouts != null)
|
||||
{
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"=== Custom Layouts ({customLayouts.CustomLayouts.Count} total) ===");
|
||||
sb.AppendLine(string.Format(CultureInfo.InvariantCulture, Properties.Resources.get_layouts_custom_header, customLayouts.CustomLayouts.Count));
|
||||
|
||||
for (int i = 0; i < customLayouts.CustomLayouts.Count; i++)
|
||||
{
|
||||
@@ -92,8 +92,8 @@ internal sealed partial class GetLayoutsCommand : FancyZonesBaseCommand
|
||||
// Add note for canvas layouts.
|
||||
if (isCanvasLayout)
|
||||
{
|
||||
sb.AppendLine("\n Note: Canvas layout preview is approximate.");
|
||||
sb.AppendLine(" Open FancyZones Editor for precise zone boundaries.");
|
||||
sb.AppendLine($"\n {Properties.Resources.get_layouts_canvas_note}");
|
||||
sb.AppendLine($" {Properties.Resources.get_layouts_canvas_detail}");
|
||||
}
|
||||
|
||||
if (i < customLayouts.CustomLayouts.Count - 1)
|
||||
@@ -102,7 +102,7 @@ internal sealed partial class GetLayoutsCommand : FancyZonesBaseCommand
|
||||
}
|
||||
}
|
||||
|
||||
sb.AppendLine("\nUse 'FancyZonesCLI.exe set-layout <UUID>' to apply a layout.");
|
||||
sb.AppendLine($"\n{Properties.Resources.get_layouts_usage}");
|
||||
}
|
||||
|
||||
return sb.ToString().TrimEnd();
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace FancyZonesCLI.CommandLine.Commands;
|
||||
internal sealed partial class GetMonitorsCommand : FancyZonesBaseCommand
|
||||
{
|
||||
public GetMonitorsCommand()
|
||||
: base("get-monitors", "List monitors and FancyZones metadata")
|
||||
: base("get-monitors", Properties.Resources.cmd_get_monitors)
|
||||
{
|
||||
AddAlias("m");
|
||||
}
|
||||
@@ -31,19 +31,19 @@ internal sealed partial class GetMonitorsCommand : FancyZonesBaseCommand
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to read monitor information. {ex.Message}{Environment.NewLine}Note: Ensure FancyZones is running to get current monitor information.", ex);
|
||||
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Properties.Resources.get_monitors_error, ex.Message), ex);
|
||||
}
|
||||
|
||||
if (editorParams.Monitors == null || editorParams.Monitors.Count == 0)
|
||||
{
|
||||
return "No monitors found.";
|
||||
return Properties.Resources.get_monitors_no_monitors;
|
||||
}
|
||||
|
||||
// Also read applied layouts to show which layout is active on each monitor.
|
||||
var appliedLayouts = FancyZonesDataIO.ReadAppliedLayouts();
|
||||
|
||||
var sb = new System.Text.StringBuilder();
|
||||
sb.AppendLine(CultureInfo.InvariantCulture, $"=== Monitors ({editorParams.Monitors.Count} total) ===");
|
||||
sb.AppendLine(string.Format(CultureInfo.InvariantCulture, Properties.Resources.get_monitors_header, editorParams.Monitors.Count));
|
||||
sb.AppendLine();
|
||||
|
||||
for (int i = 0; i < editorParams.Monitors.Count; i++)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
using System;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
@@ -13,7 +14,7 @@ namespace FancyZonesCLI.CommandLine.Commands;
|
||||
internal sealed partial class OpenEditorCommand : FancyZonesBaseCommand
|
||||
{
|
||||
public OpenEditorCommand()
|
||||
: base("open-editor", "Launch FancyZones layout editor")
|
||||
: base("open-editor", Properties.Resources.cmd_open_editor)
|
||||
{
|
||||
AddAlias("e");
|
||||
}
|
||||
@@ -38,7 +39,7 @@ internal sealed partial class OpenEditorCommand : FancyZonesBaseCommand
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to request FancyZones Editor launch. {ex.Message}", ex);
|
||||
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Properties.Resources.open_editor_error, ex.Message), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
using System;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
|
||||
namespace FancyZonesCLI.CommandLine.Commands;
|
||||
@@ -12,7 +13,7 @@ namespace FancyZonesCLI.CommandLine.Commands;
|
||||
internal sealed partial class OpenSettingsCommand : FancyZonesBaseCommand
|
||||
{
|
||||
public OpenSettingsCommand()
|
||||
: base("open-settings", "Open FancyZones settings page")
|
||||
: base("open-settings", Properties.Resources.cmd_open_settings)
|
||||
{
|
||||
AddAlias("settings");
|
||||
}
|
||||
@@ -37,14 +38,14 @@ internal sealed partial class OpenSettingsCommand : FancyZonesBaseCommand
|
||||
|
||||
if (process == null)
|
||||
{
|
||||
throw new InvalidOperationException("PowerToys.exe failed to start.");
|
||||
throw new InvalidOperationException(Properties.Resources.open_settings_error_not_started);
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to open FancyZones Settings. {ex.Message}", ex);
|
||||
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Properties.Resources.open_settings_error, ex.Message), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
using System;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.Globalization;
|
||||
|
||||
using FancyZonesEditorCommon.Data;
|
||||
using FancyZonesEditorCommon.Utils;
|
||||
@@ -16,11 +17,11 @@ internal sealed partial class RemoveHotkeyCommand : FancyZonesBaseCommand
|
||||
private readonly Argument<int> _key;
|
||||
|
||||
public RemoveHotkeyCommand()
|
||||
: base("remove-hotkey", "Remove hotkey assignment")
|
||||
: base("remove-hotkey", Properties.Resources.cmd_remove_hotkey)
|
||||
{
|
||||
AddAlias("rhk");
|
||||
|
||||
_key = new Argument<int>("key", "Hotkey index (0-9)");
|
||||
_key = new Argument<int>("key", Properties.Resources.remove_hotkey_arg_key);
|
||||
AddArgument(_key);
|
||||
}
|
||||
|
||||
@@ -33,14 +34,14 @@ internal sealed partial class RemoveHotkeyCommand : FancyZonesBaseCommand
|
||||
|
||||
if (hotkeysWrapper.LayoutHotkeys == null)
|
||||
{
|
||||
return "No hotkeys configured.";
|
||||
return Properties.Resources.remove_hotkey_no_hotkeys;
|
||||
}
|
||||
|
||||
var hotkeysList = hotkeysWrapper.LayoutHotkeys;
|
||||
var removed = hotkeysList.RemoveAll(h => h.Key == key);
|
||||
if (removed == 0)
|
||||
{
|
||||
return $"No hotkey assigned to key {key}";
|
||||
return string.Format(CultureInfo.InvariantCulture, Properties.Resources.remove_hotkey_not_found, key);
|
||||
}
|
||||
|
||||
// Save.
|
||||
|
||||
@@ -6,6 +6,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.CommandLine;
|
||||
using System.CommandLine.Invocation;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
using FancyZonesEditorCommon.Data;
|
||||
@@ -19,12 +20,12 @@ internal sealed partial class SetHotkeyCommand : FancyZonesBaseCommand
|
||||
private readonly Argument<string> _layout;
|
||||
|
||||
public SetHotkeyCommand()
|
||||
: base("set-hotkey", "Assign hotkey (0-9) to a custom layout")
|
||||
: base("set-hotkey", Properties.Resources.cmd_set_hotkey)
|
||||
{
|
||||
AddAlias("shk");
|
||||
|
||||
_key = new Argument<int>("key", "Hotkey index (0-9)");
|
||||
_layout = new Argument<string>("layout", "Custom layout UUID");
|
||||
_key = new Argument<int>("key", Properties.Resources.set_hotkey_arg_key);
|
||||
_layout = new Argument<string>("layout", Properties.Resources.set_hotkey_arg_layout);
|
||||
|
||||
AddArgument(_key);
|
||||
AddArgument(_layout);
|
||||
@@ -38,7 +39,7 @@ internal sealed partial class SetHotkeyCommand : FancyZonesBaseCommand
|
||||
|
||||
if (key < 0 || key > 9)
|
||||
{
|
||||
throw new InvalidOperationException("Key must be between 0 and 9.");
|
||||
throw new InvalidOperationException(Properties.Resources.set_hotkey_error_invalid_key);
|
||||
}
|
||||
|
||||
// Editor only allows assigning hotkeys to existing custom layouts.
|
||||
@@ -59,7 +60,7 @@ internal sealed partial class SetHotkeyCommand : FancyZonesBaseCommand
|
||||
|
||||
if (!matchedLayout.HasValue)
|
||||
{
|
||||
throw new InvalidOperationException($"Layout '{layout}' is not a custom layout UUID.");
|
||||
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Properties.Resources.set_hotkey_error_not_custom, layout));
|
||||
}
|
||||
|
||||
string layoutName = matchedLayout.Value.Name;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user