mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-07-06 10:30:29 +02:00
Compare commits
9 Commits
copilot/cr
...
dev/duhowe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb34f71b62 | ||
|
|
addebb8126 | ||
|
|
3b6453c932 | ||
|
|
2c97e04019 | ||
|
|
d27594c4f7 | ||
|
|
ac28b1c29f | ||
|
|
ee70b3ceca | ||
|
|
6fdff7a10f | ||
|
|
b78fa62ba3 |
24
.github/actions/spell-check/expect.txt
vendored
24
.github/actions/spell-check/expect.txt
vendored
@@ -35,6 +35,7 @@ Allman
|
||||
ALLOWUNDO
|
||||
ALLVIEW
|
||||
ALPHATYPE
|
||||
altkey
|
||||
AModifier
|
||||
amr
|
||||
ANDSCANS
|
||||
@@ -52,6 +53,7 @@ Apm
|
||||
APPBARDATA
|
||||
APPEXECLINK
|
||||
APPLICATIONFRAMEHOST
|
||||
apphost
|
||||
appmanifest
|
||||
APPMODEL
|
||||
APPNAME
|
||||
@@ -455,6 +457,9 @@ fdx
|
||||
FErase
|
||||
fesf
|
||||
FFFF
|
||||
fffffffzzz
|
||||
FInc
|
||||
FFh
|
||||
Figma
|
||||
FILEEXPLORER
|
||||
FILEFLAGS
|
||||
@@ -576,6 +581,7 @@ HIBYTE
|
||||
hicon
|
||||
HIDEWINDOW
|
||||
Hif
|
||||
highcontrast
|
||||
highlightbackground
|
||||
highlightthickness
|
||||
HIMAGELIST
|
||||
@@ -849,6 +855,8 @@ MDL
|
||||
mdtext
|
||||
mdtxt
|
||||
mdwn
|
||||
measuretool
|
||||
mccs
|
||||
meme
|
||||
memicmp
|
||||
MENUITEMINFO
|
||||
@@ -892,6 +900,7 @@ MONITORINFOEX
|
||||
MONITORINFOEXW
|
||||
monitorinfof
|
||||
MOUSEACTIVATE
|
||||
mousecrosshairs
|
||||
MOUSEDATA
|
||||
MOUSEEVENTF
|
||||
MOUSEHWHEEL
|
||||
@@ -923,6 +932,7 @@ mstsc
|
||||
msvcp
|
||||
MT
|
||||
MTND
|
||||
multimonitor
|
||||
MULTIPLEUSE
|
||||
multizone
|
||||
muxc
|
||||
@@ -1016,6 +1026,9 @@ NORMALDISPLAY
|
||||
NORMALUSER
|
||||
NOSEARCH
|
||||
NOSENDCHANGING
|
||||
NOSIZE
|
||||
notdefault
|
||||
Nosize
|
||||
NOTHOUSANDS
|
||||
NOTICKS
|
||||
NOTIFICATIONSDLL
|
||||
@@ -1106,6 +1119,7 @@ PCIDLIST
|
||||
PCTSTR
|
||||
PCWSTR
|
||||
PDEVMODE
|
||||
PDFs
|
||||
pdisp
|
||||
PDLL
|
||||
pdo
|
||||
@@ -1154,6 +1168,10 @@ Pokedex
|
||||
Popups
|
||||
POPUPWINDOW
|
||||
POSITIONITEM
|
||||
powerocr
|
||||
POWERBROADCAST
|
||||
powerdisplay
|
||||
POWERDISPLAYMODULEINTERFACE
|
||||
POWERRENAMECONTEXTMENU
|
||||
powerrenameinput
|
||||
POWERRENAMETEST
|
||||
@@ -1329,6 +1347,7 @@ rundll
|
||||
rungameid
|
||||
RUNLEVEL
|
||||
runtimeclass
|
||||
runtimeconfig
|
||||
runtimepack
|
||||
ruuid
|
||||
rvm
|
||||
@@ -1516,6 +1535,9 @@ STYLECHANGING
|
||||
subkeys
|
||||
sublang
|
||||
SUBMODULEUPDATE
|
||||
subresource
|
||||
suntimes
|
||||
swp
|
||||
sug
|
||||
Superbar
|
||||
sut
|
||||
@@ -1583,6 +1605,7 @@ TILEDWINDOW
|
||||
TILLSON
|
||||
timedate
|
||||
timediff
|
||||
timespan
|
||||
timeutil
|
||||
TITLEBARINFO
|
||||
Titlecase
|
||||
@@ -1733,6 +1756,7 @@ wdm
|
||||
wdp
|
||||
wdupenv
|
||||
webbrowsers
|
||||
webdev
|
||||
webpage
|
||||
websites
|
||||
wekyb
|
||||
|
||||
@@ -141,13 +141,13 @@
|
||||
"WinUI3Apps\\PowerToys.EnvironmentVariables.dll",
|
||||
"WinUI3Apps\\PowerToys.EnvironmentVariables.exe",
|
||||
|
||||
"PowerToys.ImageResizer.exe",
|
||||
"PowerToys.ImageResizer.dll",
|
||||
"WinUI3Apps\\PowerToys.ImageResizer.exe",
|
||||
"WinUI3Apps\\PowerToys.ImageResizer.dll",
|
||||
"WinUI3Apps\\PowerToys.ImageResizerCLI.exe",
|
||||
"WinUI3Apps\\PowerToys.ImageResizerCLI.dll",
|
||||
"PowerToys.ImageResizerExt.dll",
|
||||
"PowerToys.ImageResizerContextMenu.dll",
|
||||
"ImageResizerContextMenuPackage.msix",
|
||||
"WinUI3Apps\\PowerToys.ImageResizerExt.dll",
|
||||
"WinUI3Apps\\PowerToys.ImageResizerContextMenu.dll",
|
||||
"WinUI3Apps\\ImageResizerContextMenuPackage.msix",
|
||||
|
||||
"PowerToys.LightSwitchModuleInterface.dll",
|
||||
"LightSwitchService\\PowerToys.LightSwitchService.exe",
|
||||
|
||||
@@ -15,6 +15,11 @@ parameters:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
- name: codeSign
|
||||
displayName: "Enable Code Signing"
|
||||
type: boolean
|
||||
default: true
|
||||
|
||||
- name: versionNumber
|
||||
displayName: "Version Number"
|
||||
type: string
|
||||
@@ -89,7 +94,7 @@ extends:
|
||||
versionNumber: ${{ parameters.versionNumber }}
|
||||
publishArtifacts: false # 1ES PT handles publication for us.
|
||||
official: true
|
||||
codeSign: true
|
||||
codeSign: ${{ parameters.codeSign }}
|
||||
runTests: false
|
||||
buildTests: false
|
||||
signingIdentity:
|
||||
@@ -130,7 +135,7 @@ extends:
|
||||
name: SHINE-INT-L
|
||||
os: windows
|
||||
official: true
|
||||
codeSign: true
|
||||
codeSign: ${{ parameters.codeSign }}
|
||||
signingIdentity:
|
||||
serviceName: $(SigningServiceName)
|
||||
appId: $(SigningAppId)
|
||||
|
||||
@@ -59,6 +59,10 @@ _If you want to find diagnostic data events in the source code, these two links
|
||||
<td>Microsoft.PowerToys.Repair_Fail</td>
|
||||
<td>Triggered when the PowerToys repair operation fails to complete successfully due to an error.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Microsoft.PowerToys.PowerToys_ModuleLaunchedFromSettings</td>
|
||||
<td>Logs when a module editor is launched from the Settings UI or Quick Access panel, including which module was launched.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Microsoft.PowerToys.Runner_Launch</td>
|
||||
<td>Indicates when the PowerToys Runner is launched.</td>
|
||||
@@ -392,6 +396,10 @@ _If you want to find diagnostic data events in the source code, these two links
|
||||
<td>Microsoft.PowerToys.CmdPal_SessionDuration</td>
|
||||
<td>Logs session metrics from launch to dismissal including duration, commands executed, pages visited, search queries, navigation depth, and errors.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Microsoft.PowerToys.CmdPal_DockConfiguration</td>
|
||||
<td>Tracks dock configuration at startup including whether the dock is enabled, dock side (top, bottom, left, or right), and the list of extension commands pinned to the start, center, and end sections of the dock.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### Crop And Lock
|
||||
@@ -856,6 +864,10 @@ _If you want to find diagnostic data events in the source code, these two links
|
||||
<td>Microsoft.PowerToys.KeyboardManager_KeyToShortcutRemapInvoked</td>
|
||||
<td>Logs each instance of a key-to-shortcut remap being used.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Microsoft.PowerToys.KeyboardManager_LaunchEditor</td>
|
||||
<td>Logs when the Keyboard Manager editor is launched, including whether it was opened via hotkey or through settings.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Microsoft.PowerToys.KeyboardManager_OSLevelShortcutRemapCount</td>
|
||||
<td>Logs the total number of OS-level shortcut remaps configured by the user.</td>
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
# Settings resource
|
||||
Manage the settings for PowerToys modules
|
||||
|
||||
## Commands
|
||||
|
||||
### ✨ Modules
|
||||
List all the modules supported by the settings resource.
|
||||
```shell
|
||||
PS C:\> PowerToys.DSC.exe modules --resource 'settings'
|
||||
AdvancedPaste
|
||||
AlwaysOnTop
|
||||
App
|
||||
Awake
|
||||
ColorPicker
|
||||
CropAndLock
|
||||
EnvironmentVariables
|
||||
FancyZones
|
||||
FileLocksmith
|
||||
FindMyMouse
|
||||
Hosts
|
||||
ImageResizer
|
||||
KeyboardManager
|
||||
MeasureTool
|
||||
MouseHighlighter
|
||||
MouseJump
|
||||
MousePointerCrosshairs
|
||||
Peek
|
||||
PowerAccent
|
||||
PowerOCR
|
||||
PowerRename
|
||||
RegistryPreview
|
||||
ShortcutGuide
|
||||
Workspaces
|
||||
ZoomIt
|
||||
```
|
||||
|
||||
### 📄 Get
|
||||
Get the settings for a specific module.
|
||||
```shell
|
||||
PS C:\> PowerToys.DSC.exe get --resource 'settings' --module EnvironmentVariables
|
||||
{"settings":{"properties":{"LaunchAdministrator":{"value":true}},"name":"EnvironmentVariables","version":"1.0"}}
|
||||
```
|
||||
|
||||
### 🖨️ Export
|
||||
Export the settings for a specific module.
|
||||
|
||||
ℹ️ Settings resource Get and Export operation output states are identical.
|
||||
```shell
|
||||
PS C:\> PowerToys.DSC.exe get --resource 'settings' --module EnvironmentVariables
|
||||
{"settings":{"properties":{"LaunchAdministrator":{"value":true}},"name":"EnvironmentVariables","version":"1.0"}}
|
||||
```
|
||||
|
||||
### 📝 Set
|
||||
Set the settings for a specific module. This command will update the settings to the specified values.
|
||||
```shell
|
||||
PS C:\> PowerToys.DSC.exe set --resource 'settings' --module Awake --input '{"settings":{"properties":{"keepDisplayOn":false,"mode":0,"intervalHours":0,"intervalMinutes":1,"expirationDateTime":"2025-08-13T10:10:00.000001-07:00","customTrayTimes":{}},"name":"Awake","version":"0.0.1"}}'
|
||||
{"settings":{"properties":{"keepDisplayOn":false,"mode":0,"intervalHours":0,"intervalMinutes":1,"expirationDateTime":"2025-08-13T10:10:00.000001-07:00","customTrayTimes":{}},"name":"Awake","version":"0.0.1"}}
|
||||
["settings"]
|
||||
```
|
||||
|
||||
### 🧪 Test
|
||||
Test the settings for a specific module. This command will check if the current settings match the desired state.
|
||||
```shell
|
||||
PS C:\> PowerToys.DSC.exe test --resource 'settings' --module Awake --input '{"settings":{"properties":{"keepDisplayOn":false,"mode":0,"intervalHours":0,"intervalMinutes":1,"expirationDateTime":"2025-08-13T10:10:00.000002-07:00","customTrayTimes":{}},"name":"Awake","version":"0.0.1"}}'
|
||||
{"settings":{"properties":{"keepDisplayOn":false,"mode":0,"intervalHours":0,"intervalMinutes":1,"expirationDateTime":"2025-08-13T10:10:00.000001-07:00","customTrayTimes":{}},"name":"Awake","version":"0.0.1"},"_inDesiredState":false}
|
||||
["settings"]
|
||||
```
|
||||
|
||||
### 🛠️ Schema
|
||||
Generates the JSON schema for the settings resource of a specific module.
|
||||
```shell
|
||||
PS C:\> PowerToys.DSC.exe schema --resource 'settings' --module Awake
|
||||
{"$schema":"http://json-schema.org/draft-04/schema#","title":"SettingsResourceObjectOfAwakeSettings","type":"object","additionalProperties":false,"required":["settings"],"properties":{"_inDesiredState":{"type":["boolean","null"],"description":"Indicates whether an instance is in the desired state"},"settings":{"description":"The settings content for the module."}}}
|
||||
PS E:\src\powertoys> PowerToys.DSC.exe schema --resource 'settings' --module Awake | Format-Json
|
||||
```
|
||||
|
||||
### 📦 Manifest
|
||||
Generates a manifest dsc resource JSON file for the specified module.
|
||||
- If the module is not specified, it will generate a manifest for all modules.
|
||||
- If the output directory is not specified, it will print the manifest to the console.
|
||||
```shell
|
||||
PS C:\> PowerToys.DSC.exe manifest --resource settings --module 'Awake' --outputDir "C:\manifests"
|
||||
```
|
||||
263
doc/dsc/modules/AdvancedPaste.md
Normal file
263
doc/dsc/modules/AdvancedPaste.md
Normal file
@@ -0,0 +1,263 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys AdvancedPaste module
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: AdvancedPaste Module
|
||||
---
|
||||
|
||||
# AdvancedPaste Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration for the Advanced Paste utility, which provides advanced clipboard operations and custom paste formats.
|
||||
|
||||
## Description
|
||||
|
||||
The `AdvancedPaste` module configures PowerToys Advanced Paste, a utility
|
||||
that extends clipboard functionality with AI-powered transformations, custom
|
||||
formats, and advanced paste options. It allows you to paste clipboard content
|
||||
with transformations like plain text conversion, markdown formatting, JSON
|
||||
formatting, and AI-based text processing.
|
||||
|
||||
## Properties
|
||||
|
||||
The AdvancedPaste module supports the following configurable properties:
|
||||
|
||||
### IsAdvancedAIEnabled
|
||||
|
||||
Controls whether AI-powered paste transformations are enabled.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
**Description:** Enables AI-based clipboard transformations such as
|
||||
summarization, translation, and content reformatting.
|
||||
|
||||
### PasteAsPlainTextHotkey
|
||||
|
||||
Sets the keyboard shortcut for pasting as plain text.
|
||||
|
||||
**Type:** object
|
||||
**Properties:**
|
||||
|
||||
- `win` (boolean) - Windows key modifier.
|
||||
- `ctrl` (boolean) - Ctrl key modifier.
|
||||
- `alt` (boolean) - Alt key modifier.
|
||||
- `shift` (boolean) - Shift key modifier.
|
||||
- `code` (integer) - Virtual key code.
|
||||
- `key` (string) - Key name.
|
||||
|
||||
**Default:** `Ctrl+Win+V`
|
||||
|
||||
### PasteAsMarkdownHotkey
|
||||
|
||||
Sets the keyboard shortcut for pasting as markdown.
|
||||
|
||||
**Type:** object (same structure as PasteAsPlainTextHotkey)
|
||||
**Default:** `Ctrl+Win+Shift+V`
|
||||
|
||||
### PasteAsJsonHotkey
|
||||
|
||||
Sets the keyboard shortcut for pasting as JSON.
|
||||
|
||||
**Type:** object (same structure as PasteAsPlainTextHotkey)
|
||||
|
||||
### ShowCustomPreview
|
||||
|
||||
Controls whether a preview window is shown before pasting custom formats.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `true`
|
||||
|
||||
### CloseAfterLosingFocus
|
||||
|
||||
Controls whether the Advanced Paste window closes when it loses focus.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Enable AI features with direct execution
|
||||
|
||||
This example enables AI-powered paste transformations.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
IsAdvancedAIEnabled = $true
|
||||
}
|
||||
name = "AdvancedPaste"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module AdvancedPaste `
|
||||
--input $config
|
||||
```
|
||||
|
||||
### Example 2 - Configure paste hotkeys with DSC
|
||||
|
||||
This example customizes keyboard shortcuts for different paste formats.
|
||||
|
||||
```bash
|
||||
dsc config set --file advancedpaste-hotkeys.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# advancedpaste-hotkeys.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure Advanced Paste hotkeys
|
||||
type: Microsoft.PowerToys/AdvancedPasteSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
PasteAsPlainTextHotkey:
|
||||
win: true
|
||||
ctrl: true
|
||||
alt: false
|
||||
shift: false
|
||||
code: 86
|
||||
key: V
|
||||
PasteAsMarkdownHotkey:
|
||||
win: true
|
||||
ctrl: true
|
||||
alt: false
|
||||
shift: true
|
||||
code: 86
|
||||
key: V
|
||||
name: AdvancedPaste
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Install and configure with WinGet
|
||||
|
||||
This example installs PowerToys and configures Advanced Paste with AI
|
||||
enabled.
|
||||
|
||||
```bash
|
||||
winget configure winget-advancedpaste.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-advancedpaste.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Configure Advanced Paste
|
||||
type: Microsoft.PowerToys/AdvancedPasteSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
IsAdvancedAIEnabled: true
|
||||
ShowCustomPreview: true
|
||||
CloseAfterLosingFocus: true
|
||||
name: AdvancedPaste
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 4 - Enable with custom preview settings
|
||||
|
||||
This example configures preview behavior for custom paste formats.
|
||||
|
||||
```bash
|
||||
dsc config set --file advancedpaste-preview.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# advancedpaste-preview.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure preview settings
|
||||
type: Microsoft.PowerToys/AdvancedPasteSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ShowCustomPreview: true
|
||||
CloseAfterLosingFocus: false
|
||||
name: AdvancedPaste
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 5 - Test AI enablement
|
||||
|
||||
This example tests whether AI features are enabled.
|
||||
|
||||
```powershell
|
||||
$desired = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
IsAdvancedAIEnabled = $true
|
||||
}
|
||||
name = "AdvancedPaste"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
$result = PowerToys.DSC.exe test --resource 'settings' `
|
||||
--module AdvancedPaste --input $desired | ConvertFrom-Json
|
||||
|
||||
if ($result._inDesiredState) {
|
||||
Write-Host "AI features are enabled"
|
||||
} else {
|
||||
Write-Host "AI features need to be enabled"
|
||||
}
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### Development workflow
|
||||
|
||||
Enable AI transformations for code snippets and documentation:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Developer paste settings
|
||||
type: Microsoft.PowerToys/AdvancedPasteSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
IsAdvancedAIEnabled: true
|
||||
ShowCustomPreview: true
|
||||
name: AdvancedPaste
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Content creation
|
||||
|
||||
Configure for markdown and formatted text operations:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Content creator settings
|
||||
type: Microsoft.PowerToys/AdvancedPasteSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ShowCustomPreview: true
|
||||
CloseAfterLosingFocus: false
|
||||
name: AdvancedPaste
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [ColorPicker Module][03] - System-wide color picker utility
|
||||
- [PowerToys Advanced Paste Documentation][04]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./ColorPicker.md
|
||||
[04]: https://learn.microsoft.com/windows/powertoys/advanced-paste
|
||||
299
doc/dsc/modules/AlwaysOnTop.md
Normal file
299
doc/dsc/modules/AlwaysOnTop.md
Normal file
@@ -0,0 +1,299 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys AlwaysOnTop module
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: AlwaysOnTop Module
|
||||
---
|
||||
|
||||
# AlwaysOnTop Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration for the Always On Top utility, which pins windows to stay on top of other windows.
|
||||
|
||||
## Description
|
||||
|
||||
The `AlwaysOnTop` module configures PowerToys Always On Top, a utility that
|
||||
allows you to pin any window to remain visible above all other windows. This
|
||||
is useful for keeping reference materials, chat windows, or monitoring tools
|
||||
visible while working with other applications.
|
||||
|
||||
## Properties
|
||||
|
||||
The AlwaysOnTop module supports the following configurable properties:
|
||||
|
||||
### Hotkey
|
||||
|
||||
Sets the keyboard shortcut to toggle Always On Top for the active window.
|
||||
|
||||
**Type:** object
|
||||
**Properties:**
|
||||
|
||||
- `win` (boolean) - Windows key modifier.
|
||||
- `ctrl` (boolean) - Ctrl key modifier.
|
||||
- `alt` (boolean) - Alt key modifier.
|
||||
- `shift` (boolean) - Shift key modifier.
|
||||
- `code` (integer) - Virtual key code.
|
||||
- `key` (string) - Key name.
|
||||
|
||||
**Default:** `Win+Ctrl+T`
|
||||
|
||||
### FrameEnabled
|
||||
|
||||
Controls whether a colored border is displayed around pinned windows.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `true`
|
||||
|
||||
### FrameThickness
|
||||
|
||||
Sets the thickness of the border around pinned windows (in pixels).
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `1` to `100`
|
||||
**Default:** `5`
|
||||
|
||||
### FrameColor
|
||||
|
||||
Sets the color of the border around pinned windows.
|
||||
|
||||
**Type:** string (hex color)
|
||||
**Format:** `"#RRGGBB"`
|
||||
**Default:** `"#FF0000"` (red)
|
||||
|
||||
### FrameOpacity
|
||||
|
||||
Sets the opacity of the border (0-100).
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `0` to `100`
|
||||
**Default:** `100`
|
||||
|
||||
### FrameAccentColor
|
||||
|
||||
Controls whether to use the Windows accent color for the frame.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
### SoundEnabled
|
||||
|
||||
Controls whether a sound plays when toggling Always On Top.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
### DoNotActivateOnGameMode
|
||||
|
||||
Controls whether Always On Top is automatically disabled during game mode.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `true`
|
||||
|
||||
### RoundCornersEnabled
|
||||
|
||||
Controls whether the frame has rounded corners.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `true`
|
||||
|
||||
### ExcludedApps
|
||||
|
||||
List of applications excluded from Always On Top functionality.
|
||||
|
||||
**Type:** string (newline-separated list of executable names)
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Enable with default settings using direct execution
|
||||
|
||||
This example enables Always On Top with default border appearance.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
FrameEnabled = $true
|
||||
FrameThickness = 5
|
||||
FrameColor = "#FF0000"
|
||||
FrameOpacity = 100
|
||||
}
|
||||
name = "AlwaysOnTop"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module AlwaysOnTop `
|
||||
--input $config
|
||||
```
|
||||
|
||||
### Example 2 - Customize frame appearance with DSC
|
||||
|
||||
This example configures a custom border color and thickness.
|
||||
|
||||
```bash
|
||||
dsc config set --file alwaysontop-appearance.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# alwaysontop-appearance.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Customize Always On Top frame
|
||||
type: Microsoft.PowerToys/AlwaysOnTopSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
FrameEnabled: true
|
||||
FrameThickness: 8
|
||||
FrameColor: "#0078D7"
|
||||
FrameOpacity: 80
|
||||
RoundCornersEnabled: true
|
||||
name: AlwaysOnTop
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Configure with accent color using WinGet
|
||||
|
||||
This example installs PowerToys and configures Always On Top to use the
|
||||
Windows accent color.
|
||||
|
||||
```bash
|
||||
winget configure winget-alwaysontop.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-alwaysontop.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Configure Always On Top
|
||||
type: Microsoft.PowerToys/AlwaysOnTopSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
FrameEnabled: true
|
||||
FrameAccentColor: true
|
||||
FrameThickness: 6
|
||||
SoundEnabled: true
|
||||
name: AlwaysOnTop
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 4 - Disable for gaming
|
||||
|
||||
This example ensures Always On Top is disabled during game mode.
|
||||
|
||||
```yaml
|
||||
# alwaysontop-gaming.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure for gaming
|
||||
type: Microsoft.PowerToys/AlwaysOnTopSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
DoNotActivateOnGameMode: true
|
||||
name: AlwaysOnTop
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 5 - Minimal border configuration
|
||||
|
||||
This example configures a subtle, thin border.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
FrameEnabled = $true
|
||||
FrameThickness = 2
|
||||
FrameOpacity = 50
|
||||
RoundCornersEnabled = true
|
||||
}
|
||||
name = "AlwaysOnTop"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module AlwaysOnTop --input $config
|
||||
```
|
||||
|
||||
### Example 6 - Exclude specific applications
|
||||
|
||||
This example excludes certain applications from Always On Top.
|
||||
|
||||
```yaml
|
||||
# alwaysontop-exclusions.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Exclude apps from Always On Top
|
||||
type: Microsoft.PowerToys/AlwaysOnTopSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ExcludedApps: |
|
||||
Game.exe
|
||||
FullScreenApp.exe
|
||||
name: AlwaysOnTop
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### Reference material
|
||||
|
||||
Keep documentation or reference windows visible:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Reference window settings
|
||||
type: Microsoft.PowerToys/AlwaysOnTopSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
FrameEnabled: true
|
||||
FrameColor: "#00FF00"
|
||||
FrameOpacity: 60
|
||||
name: AlwaysOnTop
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Monitoring dashboards
|
||||
|
||||
Pin monitoring tools and dashboards:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Monitoring settings
|
||||
type: Microsoft.PowerToys/AlwaysOnTopSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
FrameEnabled: true
|
||||
FrameAccentColor: true
|
||||
SoundEnabled: false
|
||||
name: AlwaysOnTop
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [FancyZones Module][03] - Window layout manager
|
||||
- [PowerToys Always On Top Documentation][04]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./FancyZones.md
|
||||
[04]: https://learn.microsoft.com/windows/powertoys/always-on-top
|
||||
279
doc/dsc/modules/App.md
Normal file
279
doc/dsc/modules/App.md
Normal file
@@ -0,0 +1,279 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys App module (general settings)
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: App module
|
||||
---
|
||||
|
||||
# App Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages general PowerToys application settings, including utility enable/disable states, startup behavior, and theme preferences.
|
||||
|
||||
## Description
|
||||
|
||||
The `App` module controls global PowerToys settings that affect the entire
|
||||
application. This includes which utilities are enabled, whether PowerToys
|
||||
runs at startup, the application theme, and other general preferences.
|
||||
|
||||
Unlike other modules that configure specific utilities, the App module
|
||||
manages PowerToys-wide settings and the enabled state of all utilities.
|
||||
|
||||
## Properties
|
||||
|
||||
The App module supports the following configurable properties:
|
||||
|
||||
### Enabled
|
||||
|
||||
Controls which PowerToys utilities are enabled or disabled.
|
||||
|
||||
**Type:** Object
|
||||
**Properties:**
|
||||
|
||||
- `AdvancedPaste` (boolean) - Enable/disable Advanced Paste utility.
|
||||
- `AlwaysOnTop` (boolean) - Enable/disable Always On Top utility.
|
||||
- `Awake` (boolean) - Enable/disable Awake utility.
|
||||
- `ColorPicker` (boolean) - Enable/disable Color Picker utility.
|
||||
- `CropAndLock` (boolean) - Enable/disable Crop And Lock utility.
|
||||
- `EnvironmentVariables` (boolean) - Enable/disable Environment Variables
|
||||
utility.
|
||||
- `FancyZones` (boolean) - Enable/disable FancyZones utility.
|
||||
- `FileLocksmith` (boolean) - Enable/disable File Locksmith utility.
|
||||
- `FindMyMouse` (boolean) - Enable/disable Find My Mouse utility.
|
||||
- `Hosts` (boolean) - Enable/disable Hosts File Editor utility.
|
||||
- `ImageResizer` (boolean) - Enable/disable Image Resizer utility.
|
||||
- `KeyboardManager` (boolean) - Enable/disable Keyboard Manager utility.
|
||||
- `MeasureTool` (boolean) - Enable/disable Measure Tool utility.
|
||||
- `MouseHighlighter` (boolean) - Enable/disable Mouse Highlighter utility.
|
||||
- `MouseJump` (boolean) - Enable/disable Mouse Jump utility.
|
||||
- `MousePointerCrosshairs` (boolean) - Enable/disable Mouse Pointer
|
||||
Crosshairs utility.
|
||||
- `Peek` (boolean) - Enable/disable Peek utility.
|
||||
- `PowerAccent` (boolean) - Enable/disable Power Accent utility.
|
||||
- `PowerOCR` (boolean) - Enable/disable Power OCR utility.
|
||||
- `PowerRename` (boolean) - Enable/disable Power Rename utility.
|
||||
- `RegistryPreview` (boolean) - Enable/disable Registry Preview utility.
|
||||
- `ShortcutGuide` (boolean) - Enable/disable Shortcut Guide utility.
|
||||
- `Workspaces` (boolean) - Enable/disable Workspaces utility.
|
||||
- `ZoomIt` (boolean) - Enable/disable ZoomIt utility.
|
||||
|
||||
### startup
|
||||
|
||||
Controls whether PowerToys starts automatically when you sign in.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `true`
|
||||
|
||||
### run_elevated
|
||||
|
||||
Controls whether PowerToys runs with administrator privileges.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
### theme
|
||||
|
||||
Sets the application theme.
|
||||
|
||||
**Type:** string
|
||||
**Allowed values:** `"light"`, `"dark"`, `"system"`
|
||||
**Default:** `"system"`
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Enable specific utilities with direct execution
|
||||
|
||||
This example enables only FancyZones, PowerRename, and ColorPicker while
|
||||
disabling all others.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
Enabled = @{
|
||||
AdvancedPaste = $false
|
||||
AlwaysOnTop = $false
|
||||
Awake = $false
|
||||
ColorPicker = $true
|
||||
CropAndLock = $false
|
||||
EnvironmentVariables = $false
|
||||
FancyZones = $true
|
||||
FileLocksmith = $false
|
||||
FindMyMouse = $false
|
||||
Hosts = $false
|
||||
ImageResizer = $false
|
||||
KeyboardManager = $false
|
||||
MeasureTool = $false
|
||||
MouseHighlighter = $false
|
||||
MouseJump = $false
|
||||
MousePointerCrosshairs = $false
|
||||
Peek = $false
|
||||
PowerAccent = $false
|
||||
PowerOCR = $false
|
||||
PowerRename = $true
|
||||
RegistryPreview = $false
|
||||
ShortcutGuide = $false
|
||||
Workspaces = $false
|
||||
ZoomIt = $false
|
||||
}
|
||||
}
|
||||
name = "App"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module App --input $config
|
||||
```
|
||||
|
||||
### Example 2 - Configure startup and theme with DSC
|
||||
|
||||
This example configures PowerToys to run at startup with elevated privileges
|
||||
and use dark theme.
|
||||
|
||||
```bash
|
||||
dsc config set --file app-config.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# app-config.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure PowerToys general settings
|
||||
type: Microsoft.PowerToys/AppSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
startup: true
|
||||
run_elevated: true
|
||||
theme: dark
|
||||
name: App
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Enable all utilities with WinGet
|
||||
|
||||
This example installs PowerToys and enables all available utilities.
|
||||
|
||||
```bash
|
||||
winget configure winget-enable-all.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-enable-all.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Enable all utilities
|
||||
type: Microsoft.PowerToys/AppSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
Enabled:
|
||||
AdvancedPaste: true
|
||||
AlwaysOnTop: true
|
||||
Awake: true
|
||||
ColorPicker: true
|
||||
CropAndLock: true
|
||||
EnvironmentVariables: true
|
||||
FancyZones: true
|
||||
FileLocksmith: true
|
||||
FindMyMouse: true
|
||||
Hosts: true
|
||||
ImageResizer: true
|
||||
KeyboardManager: true
|
||||
MeasureTool: true
|
||||
MouseHighlighter: true
|
||||
MouseJump: true
|
||||
MousePointerCrosshairs: true
|
||||
Peek: true
|
||||
PowerAccent: true
|
||||
PowerOCR: true
|
||||
PowerRename: true
|
||||
RegistryPreview: true
|
||||
ShortcutGuide: true
|
||||
Workspaces: true
|
||||
ZoomIt: true
|
||||
name: App
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 4 - Test if specific utilities are enabled
|
||||
|
||||
This example tests whether FancyZones and PowerRename are enabled.
|
||||
|
||||
```powershell
|
||||
$desired = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
Enabled = @{
|
||||
FancyZones = $true
|
||||
PowerRename = $true
|
||||
}
|
||||
}
|
||||
name = "App"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
$result = PowerToys.DSC.exe test --resource 'settings' --module App `
|
||||
--input $desired | ConvertFrom-Json
|
||||
|
||||
if ($result._inDesiredState) {
|
||||
Write-Host "FancyZones and PowerRename are enabled"
|
||||
} else {
|
||||
Write-Host "Configuration needs to be updated"
|
||||
}
|
||||
```
|
||||
|
||||
### Example 5 - Individual resource for each utility
|
||||
|
||||
This example shows enabling utilities individually, which provides better granularity for complex configurations.
|
||||
|
||||
```powershell
|
||||
# Get current state
|
||||
PowerToys.DSC.exe get --resource 'settings' --module App
|
||||
|
||||
# Enable individual utilities
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
Enabled = @{
|
||||
FancyZones = $true
|
||||
}
|
||||
}
|
||||
name = "App"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module App --input $config
|
||||
```
|
||||
|
||||
### Example 6 - Get schema for App module
|
||||
|
||||
This example retrieves the complete JSON schema for the App module.
|
||||
|
||||
```powershell
|
||||
PowerToys.DSC.exe schema --resource 'settings' --module App | `
|
||||
ConvertFrom-Json | ConvertTo-Json -Depth 10
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [Awake][03]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./Awake.md
|
||||
342
doc/dsc/modules/Awake.md
Normal file
342
doc/dsc/modules/Awake.md
Normal file
@@ -0,0 +1,342 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys Awake module
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: Awake Module
|
||||
---
|
||||
|
||||
# Awake Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration for the Awake utility, which keeps your computer awake without changing power settings.
|
||||
|
||||
## Description
|
||||
|
||||
The `Awake` module configures PowerToys Awake, a utility that prevents your
|
||||
computer from going to sleep or turning off the display. This is useful
|
||||
during installations, presentations, or any scenario where you need to
|
||||
temporarily override power settings without permanently changing them.
|
||||
|
||||
Awake supports multiple modes including indefinite keep-awake, timed
|
||||
intervals, and scheduled expiration. The display can be kept on or allowed
|
||||
to turn off independently of the system sleep state.
|
||||
|
||||
## Properties
|
||||
|
||||
The Awake module supports the following configurable properties:
|
||||
|
||||
### keepDisplayOn
|
||||
|
||||
Controls whether the display remains on while Awake is active.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `true`
|
||||
**Description:** When `true`, prevents the display from turning off. When
|
||||
`false`, only prevents system sleep while allowing the display to turn off
|
||||
according to power settings.
|
||||
|
||||
### mode
|
||||
|
||||
Specifies the Awake operating mode.
|
||||
|
||||
**Type:** integer
|
||||
**Allowed values:**
|
||||
|
||||
- `0` - Off (Awake is disabled).
|
||||
- `1` - Keep awake indefinitely.
|
||||
- `2` - Keep awake for a timed interval.
|
||||
- `3` - Keep awake until a specific date/time.
|
||||
|
||||
**Default:** `0`
|
||||
|
||||
### intervalHours
|
||||
|
||||
Number of hours to keep the system awake (used when mode is `2`).
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `0` to `999`
|
||||
**Default:** `0`
|
||||
|
||||
### intervalMinutes
|
||||
|
||||
Number of minutes to keep the system awake (used when mode is `2`).
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `0` to `59`
|
||||
**Default:** `1`
|
||||
|
||||
### expirationDateTime
|
||||
|
||||
The date and time when Awake should automatically disable (used when mode is `3`).
|
||||
|
||||
**Type:** string (ISO 8601 datetime format)
|
||||
**Format:** `"YYYY-MM-DDTHH:mm:ss.fffffffzzz"`
|
||||
**Example:** `"2025-12-31T23:59:59.0000000-08:00"`
|
||||
|
||||
### customTrayTimes
|
||||
|
||||
Custom time intervals displayed in the system tray context menu for quick activation.
|
||||
|
||||
**Type:** object
|
||||
**Description:** A dictionary of custom time presets for quick access. Keys
|
||||
are display names, values are time specifications.
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Keep awake indefinitely with display on
|
||||
|
||||
This example configures Awake to keep the system and display awake
|
||||
indefinitely using direct execution.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
keepDisplayOn = $true
|
||||
mode = 1
|
||||
}
|
||||
name = "Awake"
|
||||
version = "0.0.1"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module Awake --input $config
|
||||
```
|
||||
|
||||
### Example 2 - Keep awake for 2 hours with DSC
|
||||
|
||||
This example configures a timed keep-awake period.
|
||||
|
||||
```bash
|
||||
dsc config set --file awake-timed.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# awake-timed.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure Awake for 2 hours
|
||||
type: Microsoft.PowerToys/AwakeSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
keepDisplayOn: true
|
||||
mode: 2
|
||||
intervalHours: 2
|
||||
intervalMinutes: 0
|
||||
name: Awake
|
||||
version: 0.0.1
|
||||
```
|
||||
|
||||
### Example 3 - Keep awake until specific time with WinGet
|
||||
|
||||
This example configures Awake to stay active until a specific date and time.
|
||||
|
||||
```bash
|
||||
winget configure winget-awake-scheduled.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-awake-scheduled.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Keep awake until end of workday
|
||||
type: Microsoft.PowerToys/AwakeSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
keepDisplayOn: true
|
||||
mode: 3
|
||||
expirationDateTime: "2025-10-18T17:00:00.0000000-07:00"
|
||||
name: Awake
|
||||
version: 0.0.1
|
||||
```
|
||||
|
||||
### Example 4 - Disable Awake
|
||||
|
||||
This example disables Awake using direct execution.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
mode = 0
|
||||
}
|
||||
name = "Awake"
|
||||
version = "0.0.1"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module Awake --input $config
|
||||
```
|
||||
|
||||
### Example 5 - Keep system awake but allow display to sleep
|
||||
|
||||
This example keeps the system awake while allowing the display to turn off.
|
||||
|
||||
```bash
|
||||
dsc config set --file awake-system-only.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# awake-system-only.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Keep system awake only
|
||||
type: Microsoft.PowerToys/AwakeSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
keepDisplayOn: false
|
||||
mode: 1
|
||||
name: Awake
|
||||
version: 0.0.1
|
||||
```
|
||||
|
||||
### Example 6 - Configure for presentation (4 hours)
|
||||
|
||||
This example configures Awake for a presentation scenario using WinGet.
|
||||
|
||||
```bash
|
||||
winget configure presentation-mode.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# presentation-mode.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Enable Awake for presentation
|
||||
type: Microsoft.PowerToys/AwakeSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
keepDisplayOn: true
|
||||
mode: 2
|
||||
intervalHours: 4
|
||||
intervalMinutes: 0
|
||||
name: Awake
|
||||
version: 0.0.1
|
||||
```
|
||||
|
||||
### Example 7 - Test current configuration
|
||||
|
||||
This example tests whether Awake is configured for indefinite keep-awake.
|
||||
|
||||
```powershell
|
||||
$desired = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
keepDisplayOn = $true
|
||||
mode = 1
|
||||
}
|
||||
name = "Awake"
|
||||
version = "0.0.1"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
$result = PowerToys.DSC.exe test --resource 'settings' --module Awake --input $desired | ConvertFrom-Json
|
||||
|
||||
if ($result._inDesiredState) {
|
||||
Write-Host "Awake is configured for indefinite keep-awake"
|
||||
} else {
|
||||
Write-Host "Awake configuration differs from desired state"
|
||||
}
|
||||
```
|
||||
|
||||
### Example 8 - Get current Awake configuration
|
||||
|
||||
This example retrieves the current Awake settings.
|
||||
|
||||
```powershell
|
||||
PowerToys.DSC.exe get --resource 'settings' --module Awake | ConvertFrom-Json | ConvertTo-Json -Depth 10
|
||||
```
|
||||
|
||||
### Example 9 - Get Awake schema
|
||||
|
||||
This example retrieves the JSON schema for Awake module properties.
|
||||
|
||||
```powershell
|
||||
PowerToys.DSC.exe schema --resource 'settings' --module Awake | ConvertFrom-Json | ConvertTo-Json -Depth 10
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### Development and builds
|
||||
|
||||
Keep the system awake during long-running builds or installations:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Keep awake during build
|
||||
type: Microsoft.PowerToys/AwakeSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
mode: 2
|
||||
intervalHours: 8
|
||||
intervalMinutes: 0
|
||||
keepDisplayOn: false
|
||||
name: Awake
|
||||
version: 0.0.1
|
||||
```
|
||||
|
||||
### Presentations and demos
|
||||
|
||||
Ensure the system stays awake during presentations:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Presentation mode
|
||||
type: Microsoft.PowerToys/AwakeSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
mode: 1
|
||||
keepDisplayOn: true
|
||||
name: Awake
|
||||
version: 0.0.1
|
||||
```
|
||||
|
||||
### Scheduled maintenance
|
||||
|
||||
Keep the system awake until a specific time for scheduled tasks:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Maintenance window
|
||||
type: Microsoft.PowerToys/AwakeSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
mode: 3
|
||||
expirationDateTime: "2025-10-19T02:00:00.0000000-07:00"
|
||||
keepDisplayOn: false
|
||||
name: Awake
|
||||
version: 0.0.1
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [PowerRename][03]
|
||||
- [PowerToys Awake Documentation][04]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./PowerRename.md
|
||||
[04]: https://learn.microsoft.com/windows/powertoys/awake
|
||||
307
doc/dsc/modules/ColorPicker.md
Normal file
307
doc/dsc/modules/ColorPicker.md
Normal file
@@ -0,0 +1,307 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys ColorPicker module
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: ColorPicker Module
|
||||
---
|
||||
|
||||
# ColorPicker Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration for the Color Picker utility, a system-wide color selection and identification tool.
|
||||
|
||||
## Description
|
||||
|
||||
The `ColorPicker` module configures PowerToys Color Picker, a utility that allows you to pick colors from anywhere on your screen and copy them to the clipboard in various formats. It's useful for designers, developers, and anyone working with colors.
|
||||
|
||||
## Properties
|
||||
|
||||
The ColorPicker module supports the following configurable properties:
|
||||
|
||||
### ActivationShortcut
|
||||
|
||||
Sets the keyboard shortcut to activate the color picker.
|
||||
|
||||
**Type:** object
|
||||
**Properties:**
|
||||
|
||||
- `win` (boolean) - Windows key modifier.
|
||||
- `ctrl` (boolean) - Ctrl key modifier.
|
||||
- `alt` (boolean) - Alt key modifier.
|
||||
- `shift` (boolean) - Shift key modifier.
|
||||
- `code` (integer) - Virtual key code.
|
||||
- `key` (string) - Key name.
|
||||
|
||||
**Default:** `Win+Shift+C`
|
||||
|
||||
### changecursor
|
||||
|
||||
Controls whether the cursor changes when the color picker is activated.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `true`
|
||||
|
||||
### copiedcolorrepresentation
|
||||
|
||||
Sets the default color format copied to the clipboard.
|
||||
|
||||
**Type:** string
|
||||
**Allowed values:** `"HEX"`, `"RGB"`, `"HSL"`, `"HSV"`, `"CMYK"`, `"HSB"`,
|
||||
`"HSI"`, `"HWB"`, `"NCol"`
|
||||
**Default:** `"HEX"`
|
||||
|
||||
### activationaction
|
||||
|
||||
Controls the action when the color picker activation shortcut is pressed.
|
||||
|
||||
**Type:** integer
|
||||
**Allowed values:**
|
||||
|
||||
- `0` - Open color picker and show editor
|
||||
- `1` - Open color picker only
|
||||
- `2` - Open editor only
|
||||
|
||||
**Default:** `0`
|
||||
|
||||
### showColorName
|
||||
|
||||
Controls whether color names are displayed in the color picker.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
### VisibleColorFormats
|
||||
|
||||
Defines which color formats are visible in the picker interface.
|
||||
|
||||
**Type:** object with boolean properties for each format:
|
||||
|
||||
- `HEX` (boolean)
|
||||
- `RGB` (boolean)
|
||||
- `HSL` (boolean)
|
||||
- `HSV` (boolean)
|
||||
- `CMYK` (boolean)
|
||||
- `HSB` (boolean)
|
||||
- `HSI` (boolean)
|
||||
- `HWB` (boolean)
|
||||
- `NCol` (boolean)
|
||||
- `Decimal` (boolean)
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Configure default color format with direct execution
|
||||
|
||||
This example sets the default copied color format to RGB.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
copiedcolorrepresentation = "RGB"
|
||||
}
|
||||
name = "ColorPicker"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module ColorPicker --input $config
|
||||
```
|
||||
|
||||
### Example 2 - Configure activation behavior with DSC
|
||||
|
||||
This example configures the color picker to open directly without the
|
||||
editor.
|
||||
|
||||
```bash
|
||||
dsc config set --file colorpicker-activation.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# colorpicker-activation.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure Color Picker activation
|
||||
type: Microsoft.PowerToys/ColorPickerSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
activationaction: 1
|
||||
changecursor: true
|
||||
copiedcolorrepresentation: HEX
|
||||
name: ColorPicker
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Install and configure for web development with WinGet
|
||||
|
||||
This example installs PowerToys and configures Color Picker for web
|
||||
developers.
|
||||
|
||||
```bash
|
||||
winget configure winget-colorpicker-webdev.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-colorpicker-webdev.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Configure Color Picker for web development
|
||||
type: Microsoft.PowerToys/ColorPickerSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
copiedcolorrepresentation: HEX
|
||||
showColorName: true
|
||||
changecursor: true
|
||||
VisibleColorFormats:
|
||||
HEX: true
|
||||
RGB: true
|
||||
HSL: true
|
||||
HSV: false
|
||||
CMYK: false
|
||||
name: ColorPicker
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 4 - Configure visible formats
|
||||
|
||||
This example enables only HEX, RGB, and HSL formats.
|
||||
|
||||
```bash
|
||||
dsc config set --file colorpicker-formats.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# colorpicker-formats.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure visible color formats
|
||||
type: Microsoft.PowerToys/ColorPickerSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
VisibleColorFormats:
|
||||
HEX: true
|
||||
RGB: true
|
||||
HSL: true
|
||||
HSV: false
|
||||
CMYK: false
|
||||
HSB: false
|
||||
HSI: false
|
||||
HWB: false
|
||||
NCol: false
|
||||
Decimal: false
|
||||
name: ColorPicker
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 5 - Configure for graphic design
|
||||
|
||||
This example configures Color Picker for graphic designers with CMYK support.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
copiedcolorrepresentation = "CMYK"
|
||||
showColorName = $true
|
||||
VisibleColorFormats = @{
|
||||
HEX = $true
|
||||
RGB = $true
|
||||
CMYK = $true
|
||||
HSL = $true
|
||||
HSV = $true
|
||||
}
|
||||
}
|
||||
name = "ColorPicker"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module ColorPicker --input $config
|
||||
```
|
||||
|
||||
### Example 6 - Test configuration
|
||||
|
||||
This example tests whether HEX is the default format.
|
||||
|
||||
```powershell
|
||||
$desired = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
copiedcolorrepresentation = "HEX"
|
||||
}
|
||||
name = "ColorPicker"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
$result = PowerToys.DSC.exe test --resource 'settings' --module ColorPicker `
|
||||
--input $desired | ConvertFrom-Json
|
||||
|
||||
if ($result._inDesiredState) {
|
||||
Write-Host "HEX is the default format"
|
||||
}
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### Web development
|
||||
|
||||
Configure for HTML/CSS color codes:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Web developer settings
|
||||
type: Microsoft.PowerToys/ColorPickerSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
copiedcolorrepresentation: HEX
|
||||
VisibleColorFormats:
|
||||
HEX: true
|
||||
RGB: true
|
||||
HSL: true
|
||||
name: ColorPicker
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Print design
|
||||
|
||||
Configure for CMYK color space:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Print designer settings
|
||||
type: Microsoft.PowerToys/ColorPickerSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
copiedcolorrepresentation: CMYK
|
||||
showColorName: true
|
||||
name: ColorPicker
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [ImageResizer][03]
|
||||
- [PowerToys Color Picker Documentation][04]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./ImageResizer.md
|
||||
[04]: https://learn.microsoft.com/windows/powertoys/color-picker
|
||||
195
doc/dsc/modules/CropAndLock.md
Normal file
195
doc/dsc/modules/CropAndLock.md
Normal file
@@ -0,0 +1,195 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys CropAndLock module
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: CropAndLock Module
|
||||
---
|
||||
|
||||
# CropAndLock Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration for the Crop And Lock utility, which crops and locks portions of windows.
|
||||
|
||||
## Description
|
||||
|
||||
The `CropAndLock` module configures PowerToys Crop And Lock, a utility that allows you to crop a portion of any window and keep it visible as a thumbnail. This is useful for monitoring specific parts of applications, keeping reference information visible, or focusing on particular UI elements.
|
||||
|
||||
## Properties
|
||||
|
||||
The CropAndLock module supports the following configurable properties:
|
||||
|
||||
### Hotkey
|
||||
|
||||
Sets the keyboard shortcut to activate Crop And Lock for the active window.
|
||||
|
||||
**Type:** object
|
||||
**Properties:**
|
||||
|
||||
- `win` (boolean) - Windows key modifier.
|
||||
- `ctrl` (boolean) - Ctrl key modifier.
|
||||
- `alt` (boolean) - Alt key modifier.
|
||||
- `shift` (boolean) - Shift key modifier.
|
||||
- `code` (integer) - Virtual key code.
|
||||
- `key` (string) - Key name.
|
||||
|
||||
**Default:** `Win+Ctrl+Shift+T`
|
||||
|
||||
### ReparentHotkey
|
||||
|
||||
Sets the keyboard shortcut to change the parent window of a cropped thumbnail.
|
||||
|
||||
**Type:** object (same structure as Hotkey)
|
||||
|
||||
### ThumbnailOpacity
|
||||
|
||||
Sets the opacity of cropped thumbnails (0-100).
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `0` to `100`
|
||||
**Default:** `100`
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Configure basic settings with direct execution
|
||||
|
||||
This example sets the Crop And Lock hotkey and thumbnail opacity.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
ThumbnailOpacity = 90
|
||||
}
|
||||
name = "CropAndLock"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module CropAndLock --input $config
|
||||
```
|
||||
|
||||
### Example 2 - Configure hotkeys with DSC
|
||||
|
||||
This example configures custom hotkeys for cropping and reparenting.
|
||||
|
||||
```bash
|
||||
dsc config set --file cropandlock-hotkeys.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# cropandlock-hotkeys.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure Crop And Lock hotkeys
|
||||
type: Microsoft.PowerToys/CropAndLockSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
Hotkey:
|
||||
win: true
|
||||
ctrl: true
|
||||
shift: true
|
||||
alt: false
|
||||
code: 84
|
||||
key: T
|
||||
ThumbnailOpacity: 85
|
||||
name: CropAndLock
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Install and configure with WinGet
|
||||
|
||||
This example installs PowerToys and configures Crop And Lock.
|
||||
|
||||
```bash
|
||||
winget configure winget-cropandlock.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-cropandlock.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Configure Crop And Lock
|
||||
type: Microsoft.PowerToys/CropAndLockSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ThumbnailOpacity: 75
|
||||
name: CropAndLock
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 4 - Semi-transparent thumbnails
|
||||
|
||||
This example configures thumbnails to be semi-transparent for overlay use.
|
||||
|
||||
```yaml
|
||||
# cropandlock-transparent.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Semi-transparent thumbnails
|
||||
type: Microsoft.PowerToys/CropAndLockSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ThumbnailOpacity: 60
|
||||
name: CropAndLock
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### Monitoring dashboards
|
||||
|
||||
Keep portions of monitoring tools visible:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Monitoring configuration
|
||||
type: Microsoft.PowerToys/CropAndLockSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ThumbnailOpacity: 80
|
||||
name: CropAndLock
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Reference material
|
||||
|
||||
Crop and display reference information:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Reference display
|
||||
type: Microsoft.PowerToys/CropAndLockSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ThumbnailOpacity: 95
|
||||
name: CropAndLock
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [MouseJump][03]
|
||||
- [PowerToys Crop And Lock Documentation][04]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./MouseJump.md
|
||||
[04]: https://learn.microsoft.com/windows/powertoys/crop-and-lock
|
||||
193
doc/dsc/modules/EnvironmentVariables.md
Normal file
193
doc/dsc/modules/EnvironmentVariables.md
Normal file
@@ -0,0 +1,193 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys EnvironmentVariables module
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: EnvironmentVariables Module
|
||||
---
|
||||
|
||||
# EnvironmentVariables Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration for the Environment Variables utility, a quick editor for system and user environment variables.
|
||||
|
||||
## Description
|
||||
|
||||
The `EnvironmentVariables` module configures PowerToys Environment Variables, a utility that provides an enhanced interface for viewing and editing Windows environment variables. It offers a more user-friendly alternative to the standard Windows environment variable editor.
|
||||
|
||||
## Properties
|
||||
|
||||
The EnvironmentVariables module supports the following configurable properties:
|
||||
|
||||
### LaunchAdministrator
|
||||
|
||||
Controls whether the Environment Variables editor launches with administrator privileges by default.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
**Description:** When enabled, the editor will always attempt to launch with elevated permissions, allowing editing of system-wide environment variables.
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Enable admin launch with direct execution
|
||||
|
||||
This example configures the Environment Variables editor to always launch with admin rights.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
LaunchAdministrator = $true
|
||||
}
|
||||
name = "EnvironmentVariables"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module EnvironmentVariables --input $config
|
||||
```
|
||||
|
||||
### Example 2 - Configure with DSC
|
||||
|
||||
This example enables administrator launch through DSC configuration.
|
||||
|
||||
```bash
|
||||
dsc config set --file environmentvariables-config.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# environmentvariables-config.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure Environment Variables editor
|
||||
type: Microsoft.PowerToys/EnvironmentVariablesSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
LaunchAdministrator: true
|
||||
name: EnvironmentVariables
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Install and configure with WinGet
|
||||
|
||||
This example installs PowerToys and configures Environment Variables for
|
||||
admin launch.
|
||||
|
||||
```bash
|
||||
winget configure winget-envvars.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-envvars.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Configure Environment Variables
|
||||
type: Microsoft.PowerToys/EnvironmentVariablesSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
LaunchAdministrator: true
|
||||
name: EnvironmentVariables
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 4 - Standard user mode
|
||||
|
||||
This example configures for standard user access (no elevation).
|
||||
|
||||
```bash
|
||||
dsc config set --file envvars-user.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# envvars-user.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: User-level Environment Variables
|
||||
type: Microsoft.PowerToys/EnvironmentVariablesSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
LaunchAdministrator: false
|
||||
name: EnvironmentVariables
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 5 - Test admin launch configuration
|
||||
|
||||
This example tests whether admin launch is enabled.
|
||||
|
||||
```powershell
|
||||
$desired = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
LaunchAdministrator = $true
|
||||
}
|
||||
name = "EnvironmentVariables"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
$result = PowerToys.DSC.exe test --resource 'settings' --module EnvironmentVariables --input $desired | ConvertFrom-Json
|
||||
|
||||
if ($result._inDesiredState) {
|
||||
Write-Host "Admin launch is enabled"
|
||||
}
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### System administration
|
||||
|
||||
Configure for system-wide environment variable management:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Admin configuration
|
||||
type: Microsoft.PowerToys/EnvironmentVariablesSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
LaunchAdministrator: true
|
||||
name: EnvironmentVariables
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Development workstations
|
||||
|
||||
Configure for user-level variable management:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Developer configuration
|
||||
type: Microsoft.PowerToys/EnvironmentVariablesSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
LaunchAdministrator: false
|
||||
name: EnvironmentVariables
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [Hosts][03]
|
||||
- [PowerToys Environment Variables Documentation][04]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./Hosts.md
|
||||
[04]: https://learn.microsoft.com/windows/powertoys/environment-variables
|
||||
545
doc/dsc/modules/FancyZones.md
Normal file
545
doc/dsc/modules/FancyZones.md
Normal file
@@ -0,0 +1,545 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys FancyZones module
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: FancyZones Module
|
||||
---
|
||||
|
||||
# FancyZones Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration for the FancyZones utility, a window layout manager that arranges and snaps windows into efficient layouts.
|
||||
|
||||
## Description
|
||||
|
||||
The `FancyZones` module configures PowerToys FancyZones, a window manager
|
||||
utility that helps organize windows into custom layouts called zones.
|
||||
FancyZones allows you to create multiple zone layouts for different displays
|
||||
and quickly snap windows into position using keyboard shortcuts or mouse
|
||||
actions.
|
||||
|
||||
This module controls activation methods, window behavior, zone appearance, editor settings, and other FancyZones preferences.
|
||||
|
||||
## Properties
|
||||
|
||||
The FancyZones module supports the following configurable properties:
|
||||
|
||||
### fancyzones_shiftDrag
|
||||
|
||||
Controls whether holding Shift while dragging a window activates zone snapping.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `true`
|
||||
|
||||
### fancyzones_mouseSwitch
|
||||
|
||||
Controls whether moving a window across monitors triggers zone selection.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
### fancyzones_overrideSnapHotkeys
|
||||
|
||||
Controls whether FancyZones overrides the Windows Snap hotkeys (Win+Arrow keys).
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
### fancyzones_moveWindowsAcrossMonitors
|
||||
|
||||
Controls whether moving windows between monitors is enabled.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
### fancyzones_moveWindowsBasedOnPosition
|
||||
|
||||
Controls whether windows move to zones based on cursor position rather than window position.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
### fancyzones_overlappingZonesAlgorithm
|
||||
|
||||
Determines the algorithm used when multiple zones overlap.
|
||||
|
||||
**Type:** integer
|
||||
**Allowed values:**
|
||||
|
||||
- `0` - Smallest zone
|
||||
- `1` - Largest zone
|
||||
- `2` - Positional (based on cursor/window position)
|
||||
|
||||
**Default:** `0`
|
||||
|
||||
### fancyzones_displayOrWorkAreaChange_moveWindows
|
||||
|
||||
Controls whether windows are moved to fit when display or work area changes.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
### fancyzones_zoneSetChange_flashZones
|
||||
|
||||
Controls whether zones flash briefly when the zone set changes.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
### fancyzones_zoneSetChange_moveWindows
|
||||
|
||||
Controls whether windows are automatically moved when the zone set changes.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
### fancyzones_appLastZone_moveWindows
|
||||
|
||||
Controls whether windows are moved to their last known zone when reopened.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `true`
|
||||
|
||||
### fancyzones_openWindowOnActiveMonitor
|
||||
|
||||
Controls whether newly opened windows appear on the currently active monitor.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
### fancyzones_spanZonesAcrossMonitors
|
||||
|
||||
Controls whether zones can span across multiple monitors.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
### fancyzones_makeDraggedWindowTransparent
|
||||
|
||||
Controls whether dragged windows become transparent to show zones underneath.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `true`
|
||||
|
||||
### fancyzones_zoneColor
|
||||
|
||||
Sets the color of zone areas.
|
||||
|
||||
**Type:** string (hex color)
|
||||
**Format:** `"#RRGGBB"`
|
||||
**Example:** `"#0078D7"`
|
||||
**Default:** `"#0078D7"`
|
||||
|
||||
### fancyzones_zoneBorderColor
|
||||
|
||||
Sets the color of zone borders.
|
||||
|
||||
**Type:** string (hex color)
|
||||
**Format:** `"#RRGGBB"`
|
||||
**Example:** `"#FFFFFF"`
|
||||
**Default:** `"#FFFFFF"`
|
||||
|
||||
### fancyzones_zoneHighlightColor
|
||||
|
||||
Sets the highlight color when a zone is activated.
|
||||
|
||||
**Type:** string (hex color)
|
||||
**Format:** `"#RRGGBB"`
|
||||
**Example:** `"#0078D7"`
|
||||
**Default:** `"#0078D7"`
|
||||
|
||||
### fancyzones_highlightOpacity
|
||||
|
||||
Sets the opacity of zone highlights (0-100).
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `0` to `100`
|
||||
**Default:** `50`
|
||||
|
||||
### fancyzones_editorHotkey
|
||||
|
||||
Sets the keyboard shortcut to open the FancyZones editor.
|
||||
|
||||
**Type:** object
|
||||
**Properties:**
|
||||
|
||||
- `win` (boolean) - Windows key modifier
|
||||
- `ctrl` (boolean) - Ctrl key modifier
|
||||
- `alt` (boolean) - Alt key modifier
|
||||
- `shift` (boolean) - Shift key modifier
|
||||
- `code` (integer) - Virtual key code
|
||||
- `key` (string) - Key name
|
||||
|
||||
**Default:** `Win+Shift+~`
|
||||
|
||||
### fancyzones_windowSwitching
|
||||
|
||||
Controls whether window switching with arrow keys is enabled.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `true`
|
||||
|
||||
### fancyzones_nextTabHotkey
|
||||
|
||||
Sets the keyboard shortcut to switch to the next tab/window in a zone.
|
||||
|
||||
**Type:** object (same structure as fancyzones_editorHotkey)
|
||||
|
||||
### fancyzones_prevTabHotkey
|
||||
|
||||
Sets the keyboard shortcut to switch to the previous tab/window in a zone.
|
||||
|
||||
**Type:** object (same structure as fancyzones_editorHotkey)
|
||||
|
||||
### fancyzones_excludedApps
|
||||
|
||||
List of applications excluded from FancyZones snapping.
|
||||
|
||||
**Type:** string (newline-separated list of executable names)
|
||||
**Example:** `"Notepad.exe\nCalc.exe"`
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Enable basic zone snapping with direct execution
|
||||
|
||||
This example enables Shift-drag zone snapping and mouse-based monitor switching.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
fancyzones_shiftDrag = $true
|
||||
fancyzones_mouseSwitch = $true
|
||||
}
|
||||
name = "FancyZones"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module FancyZones --input $config
|
||||
```
|
||||
|
||||
### Example 2 - Configure window movement behavior with DSC
|
||||
|
||||
This example configures how windows behave when displays or zones change.
|
||||
|
||||
```bash
|
||||
dsc config set --file fancyzones-window-behavior.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# fancyzones-window-behavior.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure FancyZones window behavior
|
||||
type: Microsoft.PowerToys/FancyZonesSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
fancyzones_displayOrWorkAreaChange_moveWindows: true
|
||||
fancyzones_zoneSetChange_moveWindows: true
|
||||
fancyzones_appLastZone_moveWindows: true
|
||||
fancyzones_moveWindowsAcrossMonitors: true
|
||||
name: FancyZones
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Customize zone appearance with WinGet
|
||||
|
||||
This example installs PowerToys and configures custom zone colors and
|
||||
opacity.
|
||||
|
||||
```bash
|
||||
winget configure winget-fancyzones-appearance.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-fancyzones-appearance.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Customize FancyZones appearance
|
||||
type: Microsoft.PowerToys/FancyZonesSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
fancyzones_zoneColor: "#2D2D30"
|
||||
fancyzones_zoneBorderColor: "#007ACC"
|
||||
fancyzones_zoneHighlightColor: "#007ACC"
|
||||
fancyzones_highlightOpacity: 75
|
||||
fancyzones_makeDraggedWindowTransparent: true
|
||||
name: FancyZones
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 4 - Override Windows Snap hotkeys
|
||||
|
||||
This example configures FancyZones to replace Windows default snap
|
||||
functionality.
|
||||
|
||||
```bash
|
||||
dsc config set --file fancyzones-snap-override.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# fancyzones-snap-override.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Override Windows Snap
|
||||
type: Microsoft.PowerToys/FancyZonesSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
fancyzones_overrideSnapHotkeys: true
|
||||
fancyzones_moveWindowsBasedOnPosition: true
|
||||
name: FancyZones
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 5 - Configure editor hotkey
|
||||
|
||||
This example changes the FancyZones editor hotkey to Ctrl+Shift+Alt+F.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
fancyzones_editorHotkey = @{
|
||||
win = $false
|
||||
ctrl = $true
|
||||
alt = $true
|
||||
shift = $true
|
||||
code = 70 # F key
|
||||
key = "F"
|
||||
}
|
||||
}
|
||||
name = "FancyZones"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module FancyZones --input $config
|
||||
```
|
||||
|
||||
### Example 6 - Exclude applications from zone snapping
|
||||
|
||||
This example configures FancyZones to ignore specific applications.
|
||||
|
||||
```yaml
|
||||
# fancyzones-exclusions.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Exclude apps from FancyZones
|
||||
type: Microsoft.PowerToys/FancyZonesSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
fancyzones_excludedApps: |
|
||||
Notepad.exe
|
||||
Calculator.exe
|
||||
mspaint.exe
|
||||
name: FancyZones
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 7 - Multi-monitor configuration
|
||||
|
||||
This example configures FancyZones for optimal multi-monitor workflow.
|
||||
|
||||
```bash
|
||||
dsc config set --file fancyzones-multimonitor.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# fancyzones-multimonitor.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Multi-monitor FancyZones setup
|
||||
type: Microsoft.PowerToys/FancyZonesSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
fancyzones_shiftDrag: true
|
||||
fancyzones_mouseSwitch: true
|
||||
fancyzones_moveWindowsAcrossMonitors: true
|
||||
fancyzones_spanZonesAcrossMonitors: false
|
||||
fancyzones_openWindowOnActiveMonitor: true
|
||||
fancyzones_displayOrWorkAreaChange_moveWindows: true
|
||||
name: FancyZones
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 8 - Complete FancyZones configuration with WinGet
|
||||
|
||||
This example shows a comprehensive FancyZones setup with installation.
|
||||
|
||||
```bash
|
||||
winget configure winget-fancyzones-complete.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-fancyzones-complete.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Enable FancyZones
|
||||
type: Microsoft.PowerToys/AppSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
Enabled:
|
||||
FancyZones: true
|
||||
name: App
|
||||
version: 1.0
|
||||
|
||||
- name: Configure FancyZones
|
||||
type: Microsoft.PowerToys/FancyZonesSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
# Activation
|
||||
fancyzones_shiftDrag: true
|
||||
fancyzones_mouseSwitch: true
|
||||
fancyzones_overrideSnapHotkeys: false
|
||||
|
||||
# Window behavior
|
||||
fancyzones_moveWindowsAcrossMonitors: true
|
||||
fancyzones_moveWindowsBasedOnPosition: false
|
||||
fancyzones_displayOrWorkAreaChange_moveWindows: true
|
||||
fancyzones_zoneSetChange_moveWindows: false
|
||||
fancyzones_appLastZone_moveWindows: true
|
||||
|
||||
# Appearance
|
||||
fancyzones_makeDraggedWindowTransparent: true
|
||||
fancyzones_zoneColor: "#0078D7"
|
||||
fancyzones_zoneBorderColor: "#FFFFFF"
|
||||
fancyzones_zoneHighlightColor: "#0078D7"
|
||||
fancyzones_highlightOpacity: 50
|
||||
|
||||
# Multi-monitor
|
||||
fancyzones_openWindowOnActiveMonitor: true
|
||||
fancyzones_spanZonesAcrossMonitors: false
|
||||
name: FancyZones
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 9 - Test FancyZones configuration
|
||||
|
||||
This example tests whether FancyZones is configured for multi-monitor use.
|
||||
|
||||
```powershell
|
||||
$desired = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
fancyzones_moveWindowsAcrossMonitors = $true
|
||||
fancyzones_openWindowOnActiveMonitor = $true
|
||||
}
|
||||
name = "FancyZones"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
$result = PowerToys.DSC.exe test --resource 'settings' --module FancyZones `
|
||||
--input $desired | ConvertFrom-Json
|
||||
|
||||
if ($result._inDesiredState) {
|
||||
Write-Host "FancyZones is configured for multi-monitor"
|
||||
} else {
|
||||
Write-Host "FancyZones configuration needs updating"
|
||||
}
|
||||
```
|
||||
|
||||
### Example 10 - Get FancyZones schema
|
||||
|
||||
This example retrieves the complete JSON schema for FancyZones properties.
|
||||
|
||||
```powershell
|
||||
PowerToys.DSC.exe schema --resource 'settings' --module FancyZones | `
|
||||
ConvertFrom-Json | ConvertTo-Json -Depth 10
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### Development workflow
|
||||
|
||||
Configure FancyZones for efficient development with IDE, browser, and terminal windows:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Developer layout
|
||||
type: Microsoft.PowerToys/FancyZonesSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
fancyzones_shiftDrag: true
|
||||
fancyzones_overrideSnapHotkeys: true
|
||||
fancyzones_appLastZone_moveWindows: true
|
||||
name: FancyZones
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Presentation mode
|
||||
|
||||
Optimize window management for presentations and screen sharing:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Presentation layout
|
||||
type: Microsoft.PowerToys/FancyZonesSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
fancyzones_openWindowOnActiveMonitor: true
|
||||
fancyzones_highlightOpacity: 30
|
||||
fancyzones_makeDraggedWindowTransparent: false
|
||||
name: FancyZones
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Home office setup
|
||||
|
||||
Configure for docking/undocking laptop scenarios:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Home office configuration
|
||||
type: Microsoft.PowerToys/FancyZonesSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
fancyzones_displayOrWorkAreaChange_moveWindows: true
|
||||
fancyzones_moveWindowsAcrossMonitors: true
|
||||
fancyzones_appLastZone_moveWindows: true
|
||||
name: FancyZones
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [Peek][03]
|
||||
- [PowerToys FancyZones Documentation][04]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./Peek.md
|
||||
[04]: https://learn.microsoft.com/windows/powertoys/fancyzones
|
||||
179
doc/dsc/modules/FileLocksmith.md
Normal file
179
doc/dsc/modules/FileLocksmith.md
Normal file
@@ -0,0 +1,179 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys FileLocksmith module
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: FileLocksmith Module
|
||||
---
|
||||
|
||||
# FileLocksmith Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration for the File Locksmith utility, which identifies
|
||||
processes that are locking files or folders.
|
||||
|
||||
## Description
|
||||
|
||||
The `FileLocksmith` module configures PowerToys File Locksmith, a Windows
|
||||
shell extension that helps identify which processes are using (locking)
|
||||
specific files or folders. It integrates with the Windows Explorer context
|
||||
menu for easy access.
|
||||
|
||||
## Properties
|
||||
|
||||
The FileLocksmith module supports the following configurable properties:
|
||||
|
||||
### ExtendedContextMenuOnly
|
||||
|
||||
Controls whether File Locksmith appears only in the extended context menu.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
**Description:** When `true`, File Locksmith only appears in the context menu
|
||||
when you hold Shift while right-clicking. When `false`, it appears in the
|
||||
standard context menu.
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Show in standard context menu with direct execution
|
||||
|
||||
This example configures File Locksmith to appear in the standard context menu.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
ExtendedContextMenuOnly = $false
|
||||
}
|
||||
name = "FileLocksmith"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module FileLocksmith `
|
||||
--input $config
|
||||
```
|
||||
|
||||
### Example 2 - Extended menu only with DSC
|
||||
|
||||
This example configures File Locksmith to appear only in the extended
|
||||
context menu.
|
||||
|
||||
```bash
|
||||
dsc config set --file filelocksmith-extended.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# filelocksmith-extended.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure File Locksmith for extended menu
|
||||
type: Microsoft.PowerToys/FileLocksmithSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ExtendedContextMenuOnly: true
|
||||
name: FileLocksmith
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Install and configure with WinGet
|
||||
|
||||
This example installs PowerToys and configures File Locksmith for standard
|
||||
menu access.
|
||||
|
||||
```bash
|
||||
winget configure winget-filelocksmith.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-filelocksmith.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Configure File Locksmith
|
||||
type: Microsoft.PowerToys/FileLocksmithSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ExtendedContextMenuOnly: false
|
||||
name: FileLocksmith
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 4 - Minimize context menu clutter
|
||||
|
||||
This example configures for extended menu to reduce clutter.
|
||||
|
||||
```bash
|
||||
dsc config set --file filelocksmith-minimal.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# filelocksmith-minimal.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Minimal context menu
|
||||
type: Microsoft.PowerToys/FileLocksmithSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ExtendedContextMenuOnly: true
|
||||
name: FileLocksmith
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### System administration
|
||||
|
||||
Quick access for troubleshooting file locks:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Admin quick access
|
||||
type: Microsoft.PowerToys/FileLocksmithSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ExtendedContextMenuOnly: false
|
||||
name: FileLocksmith
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Clean context menu
|
||||
|
||||
Reduce menu clutter for casual users:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Clean menu
|
||||
type: Microsoft.PowerToys/FileLocksmithSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ExtendedContextMenuOnly: true
|
||||
name: FileLocksmith
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [RegistryPreview][03]
|
||||
- [PowerToys File Locksmith Documentation][04]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./RegistryPreview.md
|
||||
[04]: https://learn.microsoft.com/windows/powertoys/file-locksmith
|
||||
288
doc/dsc/modules/FindMyMouse.md
Normal file
288
doc/dsc/modules/FindMyMouse.md
Normal file
@@ -0,0 +1,288 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys FindMyMouse module
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: FindMyMouse Module
|
||||
---
|
||||
|
||||
# FindMyMouse Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration for the Find My Mouse utility, which helps locate your
|
||||
mouse cursor on the screen.
|
||||
|
||||
## Description
|
||||
|
||||
The `FindMyMouse` module configures PowerToys Find My Mouse, a utility that
|
||||
highlights your mouse cursor location when you press the Ctrl key. This is
|
||||
particularly useful on large or multiple displays where the cursor can be
|
||||
difficult to locate.
|
||||
|
||||
## Properties
|
||||
|
||||
The FindMyMouse module supports the following configurable properties:
|
||||
|
||||
### DoNotActivateOnGameMode
|
||||
|
||||
Controls whether Find My Mouse is disabled during game mode.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `true`
|
||||
**Description:** When enabled, Find My Mouse will not activate when Windows
|
||||
game mode is active.
|
||||
|
||||
### BackgroundColor
|
||||
|
||||
Sets the background color of the spotlight effect.
|
||||
|
||||
**Type:** string (hex color)
|
||||
**Format:** `"#RRGGBB"`
|
||||
**Default:** `"#000000"` (black)
|
||||
|
||||
### SpotlightColor
|
||||
|
||||
Sets the color of the spotlight circle around the cursor.
|
||||
|
||||
**Type:** string (hex color)
|
||||
**Format:** `"#RRGGBB"`
|
||||
**Default:** `"#FFFFFF"` (white)
|
||||
|
||||
### OverlayOpacity
|
||||
|
||||
Sets the opacity of the background overlay (0-100).
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `0` to `100`
|
||||
**Default:** `50`
|
||||
|
||||
### SpotlightRadius
|
||||
|
||||
Sets the radius of the spotlight in pixels.
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `50` to `500`
|
||||
**Default:** `100`
|
||||
|
||||
### AnimationDurationMs
|
||||
|
||||
Sets the duration of the spotlight animation in milliseconds.
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `0` to `5000`
|
||||
**Default:** `500`
|
||||
|
||||
### SpotlightInitialZoom
|
||||
|
||||
Sets the initial zoom level of the spotlight effect.
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `100` to `1000`
|
||||
**Default:** `200`
|
||||
|
||||
### ExcludedApps
|
||||
|
||||
List of applications where Find My Mouse is disabled.
|
||||
|
||||
**Type:** string (newline-separated list of executable names)
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Configure spotlight appearance with direct execution
|
||||
|
||||
This example customizes the spotlight colors and radius.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
BackgroundColor = "#000000"
|
||||
SpotlightColor = "#00FF00"
|
||||
SpotlightRadius = 150
|
||||
OverlayOpacity = 60
|
||||
}
|
||||
name = "FindMyMouse"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module FindMyMouse --input $config
|
||||
```
|
||||
|
||||
### Example 2 - Configure animation with DSC
|
||||
|
||||
This example customizes the spotlight animation behavior.
|
||||
|
||||
```bash
|
||||
dsc config set --file findmymouse-animation.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# findmymouse-animation.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure Find My Mouse animation
|
||||
type: Microsoft.PowerToys/FindMyMouseSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
AnimationDurationMs: 750
|
||||
SpotlightInitialZoom: 300
|
||||
SpotlightRadius: 120
|
||||
name: FindMyMouse
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Install and configure with WinGet
|
||||
|
||||
This example installs PowerToys and configures Find My Mouse with custom
|
||||
colors.
|
||||
|
||||
```bash
|
||||
winget configure winget-findmymouse.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-findmymouse.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Configure Find My Mouse
|
||||
type: Microsoft.PowerToys/FindMyMouseSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
BackgroundColor: "#000000"
|
||||
SpotlightColor: "#0078D7"
|
||||
OverlayOpacity: 70
|
||||
SpotlightRadius: 140
|
||||
DoNotActivateOnGameMode: true
|
||||
name: FindMyMouse
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 4 - Subtle configuration
|
||||
|
||||
This example creates a subtle, less intrusive spotlight effect.
|
||||
|
||||
```bash
|
||||
dsc config set --file findmymouse-subtle.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# findmymouse-subtle.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Subtle spotlight
|
||||
type: Microsoft.PowerToys/FindMyMouseSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
OverlayOpacity: 30
|
||||
SpotlightRadius: 100
|
||||
AnimationDurationMs: 300
|
||||
name: FindMyMouse
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 5 - High visibility configuration
|
||||
|
||||
This example creates a high-visibility spotlight for accessibility.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
BackgroundColor = "#000000"
|
||||
SpotlightColor = "#FFFF00"
|
||||
OverlayOpacity = 80
|
||||
SpotlightRadius = 200
|
||||
SpotlightInitialZoom = 400
|
||||
}
|
||||
name = "FindMyMouse"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module FindMyMouse --input $config
|
||||
```
|
||||
|
||||
### Example 6 - Disable during gaming
|
||||
|
||||
This example ensures Find My Mouse doesn't interfere with games.
|
||||
|
||||
```bash
|
||||
dsc config set --file findmymouse-gaming.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# findmymouse-gaming.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Gaming configuration
|
||||
type: Microsoft.PowerToys/FindMyMouseSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
DoNotActivateOnGameMode: true
|
||||
name: FindMyMouse
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### Large displays
|
||||
|
||||
Configure for high visibility on large screens:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Large display configuration
|
||||
type: Microsoft.PowerToys/FindMyMouseSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
SpotlightRadius: 180
|
||||
OverlayOpacity: 70
|
||||
SpotlightColor: "#FFFFFF"
|
||||
name: FindMyMouse
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Accessibility
|
||||
|
||||
Configure for maximum visibility:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Accessibility configuration
|
||||
type: Microsoft.PowerToys/FindMyMouseSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
SpotlightColor: "#FFFF00"
|
||||
OverlayOpacity: 80
|
||||
SpotlightRadius: 200
|
||||
AnimationDurationMs: 1000
|
||||
name: FindMyMouse
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [MouseHighlighter][03]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./MouseHighlighter.md
|
||||
200
doc/dsc/modules/Hosts.md
Normal file
200
doc/dsc/modules/Hosts.md
Normal file
@@ -0,0 +1,200 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys Hosts module
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: Hosts Module
|
||||
---
|
||||
|
||||
# Hosts Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration for the Hosts File Editor utility, a quick editor for
|
||||
the Windows hosts file.
|
||||
|
||||
## Description
|
||||
|
||||
The `Hosts` module configures PowerToys Hosts File Editor, a utility that
|
||||
provides a user-friendly interface for viewing and editing the Windows hosts
|
||||
file. It simplifies the process of adding, modifying, and managing DNS
|
||||
entries in the hosts file.
|
||||
|
||||
## Properties
|
||||
|
||||
The Hosts module supports the following configurable properties:
|
||||
|
||||
### LaunchAdministrator
|
||||
|
||||
Controls whether the Hosts File Editor launches with administrator privileges
|
||||
by default.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
**Description:** When enabled, the editor will always attempt to launch with
|
||||
elevated permissions, which is required to edit the hosts file.
|
||||
|
||||
### LoopbackDuplicates
|
||||
|
||||
Controls how duplicate loopback addresses are handled.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
### AdditionalLinesPosition
|
||||
|
||||
Controls where additional lines are positioned when editing entries.
|
||||
|
||||
**Type:** integer
|
||||
**Allowed values:**
|
||||
|
||||
- `0` - Top
|
||||
- `1` - Bottom
|
||||
|
||||
**Default:** `0`
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Enable admin launch with direct execution
|
||||
|
||||
This example configures the Hosts editor to always launch with admin rights.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
LaunchAdministrator = $true
|
||||
}
|
||||
name = "Hosts"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module Hosts --input $config
|
||||
```
|
||||
|
||||
### Example 2 - Configure with DSC
|
||||
|
||||
This example enables administrator launch and configures line positioning.
|
||||
|
||||
```bash
|
||||
dsc config set --file hosts-config.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# hosts-config.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure Hosts File Editor
|
||||
type: Microsoft.PowerToys/HostsSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
LaunchAdministrator: true
|
||||
AdditionalLinesPosition: 1
|
||||
name: Hosts
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Install and configure with WinGet
|
||||
|
||||
This example installs PowerToys and configures the Hosts editor for admin
|
||||
launch.
|
||||
|
||||
```bash
|
||||
winget configure winget-hosts.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-hosts.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Configure Hosts File Editor
|
||||
type: Microsoft.PowerToys/HostsSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
LaunchAdministrator: true
|
||||
LoopbackDuplicates: false
|
||||
name: Hosts
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 4 - Development configuration
|
||||
|
||||
This example configures for development use with new entries at the bottom.
|
||||
|
||||
```bash
|
||||
dsc config set --file hosts-development.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# hosts-development.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Development hosts configuration
|
||||
type: Microsoft.PowerToys/HostsSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
LaunchAdministrator: true
|
||||
AdditionalLinesPosition: 1
|
||||
name: Hosts
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### System administration
|
||||
|
||||
Configure for frequent hosts file editing:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Admin configuration
|
||||
type: Microsoft.PowerToys/HostsSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
LaunchAdministrator: true
|
||||
name: Hosts
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Web development
|
||||
|
||||
Configure for development environment management:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Developer configuration
|
||||
type: Microsoft.PowerToys/HostsSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
LaunchAdministrator: true
|
||||
AdditionalLinesPosition: 1
|
||||
name: Hosts
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [KeyboardManager][03]
|
||||
- [PowerToys Hosts File Editor Documentation][04]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./KeyboardManager.md
|
||||
[04]: https://learn.microsoft.com/windows/powertoys/hosts-file-editor
|
||||
349
doc/dsc/modules/ImageResizer.md
Normal file
349
doc/dsc/modules/ImageResizer.md
Normal file
@@ -0,0 +1,349 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys ImageResizer module
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: ImageResizer Module
|
||||
---
|
||||
|
||||
# ImageResizer Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration for the Image Resizer utility, which provides quick
|
||||
image resizing from the Windows Explorer context menu.
|
||||
|
||||
## Description
|
||||
|
||||
The `ImageResizer` module configures PowerToys Image Resizer, a Windows shell
|
||||
extension that allows you to resize one or multiple images directly from the
|
||||
File Explorer context menu. It supports custom size presets and various
|
||||
resize options.
|
||||
|
||||
## Properties
|
||||
|
||||
The ImageResizer module supports the following configurable properties:
|
||||
|
||||
### ImageResizerSizes
|
||||
|
||||
Defines the preset sizes available in the Image Resizer interface.
|
||||
|
||||
**Type:** array of objects
|
||||
**Object properties:**
|
||||
|
||||
- `Name` (string) - Display name for the preset
|
||||
- `Width` (integer) - Width value
|
||||
- `Height` (integer) - Height value
|
||||
- `Unit` (string) - Unit of measurement: `"Pixel"`, `"Percent"`, `"Centimeter"`, `"Inch"`
|
||||
- `Fit` (string) - Resize mode: `"Fit"`, `"Fill"`, `"Stretch"`
|
||||
|
||||
### ImageresizerSelectedSizeIndex
|
||||
|
||||
Sets the default selected size preset (0-based index).
|
||||
|
||||
**Type:** integer
|
||||
**Default:** `0`
|
||||
|
||||
### ImageresizerShrinkOnly
|
||||
|
||||
Controls whether images are only resized if they're larger than the target size.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
### ImageresizerReplace
|
||||
|
||||
Controls whether resized images replace the original files.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
### ImageresizerIgnoreOrientation
|
||||
|
||||
Controls whether EXIF orientation data is ignored.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `true`
|
||||
|
||||
### ImageresizerJpegQualityLevel
|
||||
|
||||
Sets the JPEG quality level for resized images (1-100).
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `1` to `100`
|
||||
**Default:** `90`
|
||||
|
||||
### ImageresizerPngInterlaceOption
|
||||
|
||||
Sets the PNG interlace option.
|
||||
|
||||
**Type:** integer
|
||||
**Allowed values:**
|
||||
|
||||
- `0` - No interlacing
|
||||
- `1` - Interlaced
|
||||
|
||||
**Default:** `0`
|
||||
|
||||
### ImageresizerTiffCompressOption
|
||||
|
||||
Sets the TIFF compression option.
|
||||
|
||||
**Type:** integer
|
||||
**Allowed values:**
|
||||
|
||||
- `0` - No compression
|
||||
- `1` - LZW compression
|
||||
- `2` - ZIP compression
|
||||
|
||||
**Default:** `0`
|
||||
|
||||
### ImageresizerFileName
|
||||
|
||||
Sets the naming pattern for resized images.
|
||||
|
||||
**Type:** string
|
||||
**Default:** `"%1 (%2)"`
|
||||
**Placeholders:**
|
||||
|
||||
- `%1` - Original filename
|
||||
- `%2` - Size name
|
||||
- `%3` - Selected width
|
||||
- `%4` - Selected height
|
||||
- `%5` - Actual width
|
||||
- `%6` - Actual height
|
||||
|
||||
### ImageresizerKeepDateModified
|
||||
|
||||
Controls whether the original file's modified date is preserved.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
### ImageresizerFallbackEncoder
|
||||
|
||||
Sets the fallback encoder for unsupported formats.
|
||||
|
||||
**Type:** string
|
||||
**Allowed values:** `"png"`, `"jpg"`, `"bmp"`, `"tiff"`, `"gif"`
|
||||
**Default:** `"png"`
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Configure custom size presets with direct execution
|
||||
|
||||
This example defines custom image resize presets.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
ImageResizerSizes = @(
|
||||
@{
|
||||
Name = "Small"
|
||||
Width = 640
|
||||
Height = 480
|
||||
Unit = "Pixel"
|
||||
Fit = "Fit"
|
||||
},
|
||||
@{
|
||||
Name = "Medium"
|
||||
Width = 1280
|
||||
Height = 720
|
||||
Unit = "Pixel"
|
||||
Fit = "Fit"
|
||||
},
|
||||
@{
|
||||
Name = "Large"
|
||||
Width = 1920
|
||||
Height = 1080
|
||||
Unit = "Pixel"
|
||||
Fit = "Fit"
|
||||
}
|
||||
)
|
||||
}
|
||||
name = "ImageResizer"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module ImageResizer `
|
||||
--input $config
|
||||
```
|
||||
|
||||
### Example 2 - Configure quality settings with DSC
|
||||
|
||||
This example configures image quality and format options.
|
||||
|
||||
```bash
|
||||
dsc config set --file imageresizer-quality.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# imageresizer-quality.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure Image Resizer quality
|
||||
type: Microsoft.PowerToys/ImageResizerSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ImageresizerJpegQualityLevel: 95
|
||||
ImageresizerShrinkOnly: true
|
||||
ImageresizerKeepDateModified: true
|
||||
name: ImageResizer
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Install and configure with WinGet
|
||||
|
||||
This example installs PowerToys and configures Image Resizer with
|
||||
web-optimized presets.
|
||||
|
||||
```bash
|
||||
winget configure winget-imageresizer.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-imageresizer.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Configure Image Resizer
|
||||
type: Microsoft.PowerToys/ImageResizerSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ImageResizerSizes:
|
||||
- Name: Thumbnail
|
||||
Width: 320
|
||||
Height: 240
|
||||
Unit: Pixel
|
||||
Fit: Fit
|
||||
- Name: Web Small
|
||||
Width: 800
|
||||
Height: 600
|
||||
Unit: Pixel
|
||||
Fit: Fit
|
||||
- Name: Web Large
|
||||
Width: 1920
|
||||
Height: 1080
|
||||
Unit: Pixel
|
||||
Fit: Fit
|
||||
ImageresizerJpegQualityLevel: 85
|
||||
ImageresizerFileName: "%1_resized_%2"
|
||||
name: ImageResizer
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 4 - Photography workflow
|
||||
|
||||
This example configures for photography with high quality and metadata
|
||||
preservation.
|
||||
|
||||
```bash
|
||||
dsc config set --file imageresizer-photo.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# imageresizer-photography.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Photography configuration
|
||||
type: Microsoft.PowerToys/ImageResizerSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ImageresizerJpegQualityLevel: 100
|
||||
ImageresizerKeepDateModified: true
|
||||
ImageresizerIgnoreOrientation: false
|
||||
ImageresizerShrinkOnly: true
|
||||
name: ImageResizer
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 5 - Social media presets
|
||||
|
||||
This example defines presets for social media platforms.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
ImageResizerSizes = @(
|
||||
@{ Name = "Instagram Square"; Width = 1080; Height = 1080; Unit = "Pixel"; Fit = "Fill" },
|
||||
@{ Name = "Instagram Portrait"; Width = 1080; Height = 1350; Unit = "Pixel"; Fit = "Fill" },
|
||||
@{ Name = "Facebook Cover"; Width = 820; Height = 312; Unit = "Pixel"; Fit = "Fill" },
|
||||
@{ Name = "Twitter Header"; Width = 1500; Height = 500; Unit = "Pixel"; Fit = "Fill" }
|
||||
)
|
||||
ImageresizerJpegQualityLevel = 90
|
||||
}
|
||||
name = "ImageResizer"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module ImageResizer `
|
||||
--input $config
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### Web development
|
||||
|
||||
Configure for web-optimized images:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Web optimization
|
||||
type: Microsoft.PowerToys/ImageResizerSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ImageresizerJpegQualityLevel: 85
|
||||
ImageresizerShrinkOnly: true
|
||||
name: ImageResizer
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Content creation
|
||||
|
||||
Configure for social media and content platforms:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Content creation
|
||||
type: Microsoft.PowerToys/ImageResizerSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ImageResizerSizes:
|
||||
- Name: HD
|
||||
Width: 1920
|
||||
Height: 1080
|
||||
Unit: Pixel
|
||||
Fit: Fit
|
||||
ImageresizerJpegQualityLevel: 90
|
||||
name: ImageResizer
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [MeasureTool][03]
|
||||
- [PowerToys Image Resizer Documentation][04]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./MeasureTool.md
|
||||
[04]: https://learn.microsoft.com/windows/powertoys/image-resizer
|
||||
145
doc/dsc/modules/KeyboardManager.md
Normal file
145
doc/dsc/modules/KeyboardManager.md
Normal file
@@ -0,0 +1,145 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys KeyboardManager module
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: KeyboardManager Module
|
||||
---
|
||||
|
||||
# KeyboardManager Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration for the Keyboard Manager utility, which allows key
|
||||
remapping and custom keyboard shortcuts.
|
||||
|
||||
## Description
|
||||
|
||||
The `KeyboardManager` module configures PowerToys Keyboard Manager, a utility
|
||||
that enables you to remap keys and create custom keyboard shortcuts. It
|
||||
allows reassigning keys, creating application-specific remappings, and
|
||||
defining shortcuts that run programs or commands.
|
||||
|
||||
## Properties
|
||||
|
||||
The KeyboardManager module supports the following configurable properties:
|
||||
|
||||
### Enabled
|
||||
|
||||
Controls whether Keyboard Manager is enabled.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `true`
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Enable Keyboard Manager with direct execution
|
||||
|
||||
This example enables the Keyboard Manager utility.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
Enabled = $true
|
||||
}
|
||||
name = "KeyboardManager"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module KeyboardManager --input $config
|
||||
```
|
||||
|
||||
### Example 2 - Configure with DSC
|
||||
|
||||
This example enables Keyboard Manager through DSC configuration.
|
||||
|
||||
```bash
|
||||
dsc config set --file keyboardmanager-config.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# keyboardmanager-config.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Enable Keyboard Manager
|
||||
type: Microsoft.PowerToys/KeyboardManagerSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
Enabled: true
|
||||
name: KeyboardManager
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Install and configure with WinGet
|
||||
|
||||
This example installs PowerToys and enables Keyboard Manager.
|
||||
|
||||
```bash
|
||||
winget configure winget-keyboardmanager.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-keyboardmanager.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Enable Keyboard Manager
|
||||
type: Microsoft.PowerToys/KeyboardManagerSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
Enabled: true
|
||||
name: KeyboardManager
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## Important notes
|
||||
|
||||
> **Note:** The Keyboard Manager module DSC configuration controls the enabled state only. Key remappings and shortcut definitions are managed through the Keyboard Manager UI and stored separately. This design ensures that complex remapping configurations are not accidentally overwritten by DSC operations.
|
||||
|
||||
To configure key remappings:
|
||||
1. Enable Keyboard Manager using DSC
|
||||
2. Open PowerToys Settings
|
||||
3. Navigate to Keyboard Manager
|
||||
4. Use "Remap a key" or "Remap a shortcut" to configure specific mappings
|
||||
|
||||
## Use cases
|
||||
|
||||
### Enable for deployment
|
||||
|
||||
Enable Keyboard Manager on new workstations:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Enable Keyboard Manager
|
||||
type: Microsoft.PowerToys/KeyboardManagerSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
Enabled: true
|
||||
name: KeyboardManager
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [PowerOCR][03]
|
||||
- [PowerToys Keyboard Manager Documentation][04]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./PowerOCR.md
|
||||
[04]: https://learn.microsoft.com/windows/powertoys/keyboard-manager
|
||||
254
doc/dsc/modules/MeasureTool.md
Normal file
254
doc/dsc/modules/MeasureTool.md
Normal file
@@ -0,0 +1,254 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys MeasureTool module
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: MeasureTool Module
|
||||
---
|
||||
|
||||
# MeasureTool Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration for the Measure Tool (Screen Ruler) utility, which
|
||||
measures pixels on your screen.
|
||||
|
||||
## Description
|
||||
|
||||
The `MeasureTool` module configures PowerToys Measure Tool (also known as
|
||||
Screen Ruler), a utility that allows you to measure the distance between two
|
||||
points on your screen in pixels. It's useful for designers, developers, and
|
||||
anyone who needs to measure UI elements or screen distances.
|
||||
|
||||
## Properties
|
||||
|
||||
The MeasureTool module supports the following configurable properties:
|
||||
|
||||
### ActivationShortcut
|
||||
|
||||
Sets the keyboard shortcut to activate the measure tool.
|
||||
|
||||
**Type:** object
|
||||
**Properties:**
|
||||
|
||||
- `win` (boolean) - Windows key modifier
|
||||
- `ctrl` (boolean) - Ctrl key modifier
|
||||
- `alt` (boolean) - Alt key modifier
|
||||
- `shift` (boolean) - Shift key modifier
|
||||
- `code` (integer) - Virtual key code
|
||||
- `key` (string) - Key name
|
||||
|
||||
**Default:** `Win+Shift+M`
|
||||
|
||||
### ContinuousCapture
|
||||
|
||||
Controls whether continuous capture mode is enabled.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
### DrawFeetOnCross
|
||||
|
||||
Controls whether measurement lines extend to screen edges.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `true`
|
||||
|
||||
### PerColorChannelEdgeDetection
|
||||
|
||||
Controls whether edge detection is per-color-channel or luminosity-based.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
### PixelTolerance
|
||||
|
||||
Sets the pixel tolerance for edge detection (0-255).
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `0` to `255`
|
||||
**Default:** `30`
|
||||
|
||||
### MeasureCrossColor
|
||||
|
||||
Sets the color of the measurement crosshair.
|
||||
|
||||
**Type:** string (hex color)
|
||||
**Format:** `"#RRGGBBAA"` (with alpha)
|
||||
**Default:** `"#FF4500FF"`
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Configure activation shortcut with direct execution
|
||||
|
||||
This example customizes the measure tool activation shortcut.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
ActivationShortcut = @{
|
||||
win = $true
|
||||
ctrl = $false
|
||||
alt = $false
|
||||
shift = $true
|
||||
code = 77
|
||||
key = "M"
|
||||
}
|
||||
}
|
||||
name = "MeasureTool"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module MeasureTool `
|
||||
--input $config
|
||||
```
|
||||
|
||||
### Example 2 - Configure measurement appearance with DSC
|
||||
|
||||
This example customizes the crosshair color and measurement behavior.
|
||||
|
||||
```bash
|
||||
dsc config set --file measuretool-appearance.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# measuretool-appearance.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure Measure Tool appearance
|
||||
type: Microsoft.PowerToys/MeasureToolSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
MeasureCrossColor: "#00FF00FF"
|
||||
DrawFeetOnCross: true
|
||||
ContinuousCapture: false
|
||||
name: MeasureTool
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Install and configure with WinGet
|
||||
|
||||
This example installs PowerToys and configures Measure Tool with edge
|
||||
detection.
|
||||
|
||||
```bash
|
||||
winget configure winget-measuretool.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-measuretool.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Configure Measure Tool
|
||||
type: Microsoft.PowerToys/MeasureToolSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
PixelTolerance: 20
|
||||
PerColorChannelEdgeDetection: true
|
||||
DrawFeetOnCross: true
|
||||
name: MeasureTool
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 4 - High contrast configuration
|
||||
|
||||
This example configures for high visibility measurements.
|
||||
|
||||
```bash
|
||||
dsc config set --file measuretool-highcontrast.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# measuretool-highcontrast.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: High contrast Measure Tool
|
||||
type: Microsoft.PowerToys/MeasureToolSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
MeasureCrossColor: "#FFFF00FF"
|
||||
DrawFeetOnCross: true
|
||||
name: MeasureTool
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 5 - Continuous capture mode
|
||||
|
||||
This example enables continuous capture for repeated measurements.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
ContinuousCapture = $true
|
||||
PixelTolerance = 25
|
||||
}
|
||||
name = "MeasureTool"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module MeasureTool --input $config
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### UI/UX design
|
||||
|
||||
Configure for design work with precise measurements:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Design configuration
|
||||
type: Microsoft.PowerToys/MeasureToolSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
PixelTolerance: 15
|
||||
DrawFeetOnCross: true
|
||||
name: MeasureTool
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Web development
|
||||
|
||||
Configure for layout debugging:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Developer configuration
|
||||
type: Microsoft.PowerToys/MeasureToolSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ContinuousCapture: true
|
||||
MeasureCrossColor: "#0078D7FF"
|
||||
name: MeasureTool
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [PowerAccent][03]
|
||||
- [PowerToys Screen Ruler Documentation][04]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./PowerAccent.md
|
||||
[04]: https://learn.microsoft.com/windows/powertoys/screen-ruler
|
||||
276
doc/dsc/modules/MouseHighlighter.md
Normal file
276
doc/dsc/modules/MouseHighlighter.md
Normal file
@@ -0,0 +1,276 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys MouseHighlighter module
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: MouseHighlighter Module
|
||||
---
|
||||
|
||||
# MouseHighlighter Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration for the Mouse Highlighter utility, which highlights
|
||||
your mouse cursor and clicks.
|
||||
|
||||
## Description
|
||||
|
||||
The `MouseHighlighter` module configures PowerToys Mouse Highlighter, a
|
||||
utility that adds visual highlights to your mouse cursor and click locations.
|
||||
This is useful for presentations, tutorials, screen recordings, or
|
||||
accessibility purposes.
|
||||
|
||||
## Properties
|
||||
|
||||
The MouseHighlighter module supports the following configurable properties:
|
||||
|
||||
### ActivationShortcut
|
||||
|
||||
Sets the keyboard shortcut to toggle mouse highlighting.
|
||||
|
||||
**Type:** object
|
||||
**Properties:**
|
||||
|
||||
- `win` (boolean) - Windows key modifier
|
||||
- `ctrl` (boolean) - Ctrl key modifier
|
||||
- `alt` (boolean) - Alt key modifier
|
||||
- `shift` (boolean) - Shift key modifier
|
||||
- `code` (integer) - Virtual key code
|
||||
- `key` (string) - Key name
|
||||
|
||||
**Default:** `Win+Shift+H`
|
||||
|
||||
### LeftButtonClickColor
|
||||
|
||||
Sets the color for left mouse button clicks.
|
||||
|
||||
**Type:** string (hex color)
|
||||
**Format:** `"#RRGGBB"`
|
||||
**Default:** `"#FFFF00"` (yellow)
|
||||
|
||||
### RightButtonClickColor
|
||||
|
||||
Sets the color for right mouse button clicks.
|
||||
|
||||
**Type:** string (hex color)
|
||||
**Format:** `"#RRGGBB"`
|
||||
**Default:** `"#0000FF"` (blue)
|
||||
|
||||
### HighlightOpacity
|
||||
|
||||
Sets the opacity of click highlights (0-100).
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `0` to `100`
|
||||
**Default:** `160`
|
||||
|
||||
### HighlightRadius
|
||||
|
||||
Sets the radius of click highlights in pixels.
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `1` to `500`
|
||||
**Default:** `20`
|
||||
|
||||
### HighlightFadeDelayMs
|
||||
|
||||
Sets how long highlights remain visible in milliseconds.
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `0` to `10000`
|
||||
**Default:** `500`
|
||||
|
||||
### HighlightFadeDurationMs
|
||||
|
||||
Sets the duration of the highlight fade animation in milliseconds.
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `0` to `10000`
|
||||
**Default:** `250`
|
||||
|
||||
### AutoActivate
|
||||
|
||||
Controls whether Mouse Highlighter activates automatically during presentations.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Configure highlight colors with direct execution
|
||||
|
||||
This example customizes the click highlight colors.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
LeftButtonClickColor = "#00FF00"
|
||||
RightButtonClickColor = "#FF0000"
|
||||
HighlightOpacity = 200
|
||||
}
|
||||
name = "MouseHighlighter"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module MouseHighlighter `
|
||||
--input $config
|
||||
```
|
||||
|
||||
### Example 2 - Configure highlight animation with DSC
|
||||
|
||||
This example customizes the animation timing and appearance.
|
||||
|
||||
```bash
|
||||
dsc config set --file mousehighlighter-animation.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# mousehighlighter-animation.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure Mouse Highlighter animation
|
||||
type: Microsoft.PowerToys/MouseHighlighterSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
HighlightRadius: 30
|
||||
HighlightFadeDelayMs: 750
|
||||
HighlightFadeDurationMs: 400
|
||||
name: MouseHighlighter
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Install and configure for presentations with WinGet
|
||||
|
||||
This example installs PowerToys and configures Mouse Highlighter for
|
||||
presentations.
|
||||
|
||||
```bash
|
||||
winget configure winget-mousehighlighter.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-mousehighlighter.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Configure Mouse Highlighter for presentations
|
||||
type: Microsoft.PowerToys/MouseHighlighterSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
LeftButtonClickColor: "#FFD700"
|
||||
RightButtonClickColor: "#FF4500"
|
||||
HighlightOpacity: 220
|
||||
HighlightRadius: 25
|
||||
AutoActivate: true
|
||||
name: MouseHighlighter
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 4 - Subtle highlighting
|
||||
|
||||
This example configures subtle, less distracting highlights.
|
||||
|
||||
```bash
|
||||
dsc config set --file mousehighlighter-subtle.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# mousehighlighter-subtle.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Subtle mouse highlighting
|
||||
type: Microsoft.PowerToys/MouseHighlighterSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
HighlightOpacity: 100
|
||||
HighlightRadius: 15
|
||||
HighlightFadeDelayMs: 300
|
||||
name: MouseHighlighter
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 5 - High visibility for accessibility
|
||||
|
||||
This example configures high-contrast, long-lasting highlights.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
LeftButtonClickColor = "#FFFFFF"
|
||||
RightButtonClickColor = "#FF0000"
|
||||
HighlightOpacity = 255
|
||||
HighlightRadius = 40
|
||||
HighlightFadeDelayMs = 1500
|
||||
HighlightFadeDurationMs = 500
|
||||
}
|
||||
name = "MouseHighlighter"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module MouseHighlighter --input $config
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### Presentations and demos
|
||||
|
||||
Configure for clear visibility during presentations:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Presentation highlighting
|
||||
type: Microsoft.PowerToys/MouseHighlighterSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
LeftButtonClickColor: "#FFD700"
|
||||
HighlightOpacity: 200
|
||||
HighlightRadius: 25
|
||||
AutoActivate: true
|
||||
name: MouseHighlighter
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Screen recording
|
||||
|
||||
Configure for video tutorials and recordings:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Recording configuration
|
||||
type: Microsoft.PowerToys/MouseHighlighterSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
HighlightOpacity: 180
|
||||
HighlightFadeDelayMs: 600
|
||||
name: MouseHighlighter
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [MousePointerCrosshairs][03]
|
||||
- [PowerToys Mouse Utilities Documentation][04]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./MousePointerCrosshairs.md
|
||||
[04]: https://learn.microsoft.com/windows/powertoys/mouse-utilities
|
||||
220
doc/dsc/modules/MouseJump.md
Normal file
220
doc/dsc/modules/MouseJump.md
Normal file
@@ -0,0 +1,220 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys MouseJump module
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: MouseJump Module
|
||||
---
|
||||
|
||||
# MouseJump Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration for the Mouse Jump utility, which enables quick
|
||||
navigation across large or multiple displays.
|
||||
|
||||
## Description
|
||||
|
||||
The `MouseJump` module configures PowerToys Mouse Jump, a utility that
|
||||
provides a miniature preview of all your displays, allowing you to quickly
|
||||
jump your mouse cursor to any location. This is particularly useful with
|
||||
large monitors or multi-monitor setups.
|
||||
|
||||
## Properties
|
||||
|
||||
The MouseJump module supports the following configurable properties:
|
||||
|
||||
### ActivationShortcut
|
||||
|
||||
Sets the keyboard shortcut to activate Mouse Jump.
|
||||
|
||||
**Type:** object
|
||||
**Properties:**
|
||||
|
||||
- `win` (boolean) - Windows key modifier
|
||||
- `ctrl` (boolean) - Ctrl key modifier
|
||||
- `alt` (boolean) - Alt key modifier
|
||||
- `shift` (boolean) - Shift key modifier
|
||||
- `code` (integer) - Virtual key code
|
||||
- `key` (string) - Key name
|
||||
|
||||
**Default:** `Win+Shift+D`
|
||||
|
||||
### ThumbnailSize
|
||||
|
||||
Sets the size of the screen thumbnail preview.
|
||||
|
||||
**Type:** string
|
||||
**Allowed values:**
|
||||
|
||||
- `"small"` - Smaller thumbnail for faster performance
|
||||
- `"medium"` - Balanced size and performance
|
||||
- `"large"` - Larger thumbnail for better visibility
|
||||
|
||||
**Default:** `"medium"`
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Configure activation shortcut with direct execution
|
||||
|
||||
This example customizes the Mouse Jump activation shortcut.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
ActivationShortcut = @{
|
||||
win = $true
|
||||
ctrl = $false
|
||||
alt = $false
|
||||
shift = $true
|
||||
code = 68
|
||||
key = "D"
|
||||
}
|
||||
}
|
||||
name = "MouseJump"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module MouseJump `
|
||||
--input $config
|
||||
```
|
||||
|
||||
### Example 2 - Configure thumbnail size with DSC
|
||||
|
||||
This example sets a larger thumbnail for better visibility.
|
||||
|
||||
```bash
|
||||
dsc config set --file mousejump-size.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# mousejump-size.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure Mouse Jump thumbnail
|
||||
type: Microsoft.PowerToys/MouseJumpSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ThumbnailSize: large
|
||||
name: MouseJump
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Install and configure with WinGet
|
||||
|
||||
This example installs PowerToys and configures Mouse Jump for multi-monitor
|
||||
setups.
|
||||
|
||||
```bash
|
||||
winget configure winget-mousejump.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-mousejump.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Configure Mouse Jump
|
||||
type: Microsoft.PowerToys/MouseJumpSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ThumbnailSize: medium
|
||||
name: MouseJump
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 4 - Performance-optimized configuration
|
||||
|
||||
This example uses a smaller thumbnail for better performance.
|
||||
|
||||
```bash
|
||||
dsc config set --file mousejump-performance.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# mousejump-performance.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Performance-optimized Mouse Jump
|
||||
type: Microsoft.PowerToys/MouseJumpSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ThumbnailSize: small
|
||||
name: MouseJump
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 5 - Large display configuration
|
||||
|
||||
This example configures for large or high-DPI displays.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
ThumbnailSize = "large"
|
||||
}
|
||||
name = "MouseJump"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module MouseJump --input $config
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### Multi-monitor workstations
|
||||
|
||||
Configure for efficient navigation across multiple displays:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Multi-monitor configuration
|
||||
type: Microsoft.PowerToys/MouseJumpSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ThumbnailSize: medium
|
||||
name: MouseJump
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Large displays
|
||||
|
||||
Configure for ultra-wide or 4K+ displays:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Large display configuration
|
||||
type: Microsoft.PowerToys/MouseJumpSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ThumbnailSize: large
|
||||
name: MouseJump
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [FindMyMouse][03]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./FindMyMouse.md
|
||||
290
doc/dsc/modules/MousePointerCrosshairs.md
Normal file
290
doc/dsc/modules/MousePointerCrosshairs.md
Normal file
@@ -0,0 +1,290 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys MousePointerCrosshairs module
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: MousePointerCrosshairs Module
|
||||
---
|
||||
|
||||
# MousePointerCrosshairs Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration for the Mouse Pointer Crosshairs utility, which
|
||||
displays crosshairs centered on your mouse pointer.
|
||||
|
||||
## Description
|
||||
|
||||
The `MousePointerCrosshairs` module configures PowerToys Mouse Pointer
|
||||
Crosshairs, a utility that displays customizable crosshairs overlaid on your
|
||||
screen, centered on the mouse cursor. This is useful for presentations,
|
||||
design work, or improving cursor visibility.
|
||||
|
||||
## Properties
|
||||
|
||||
The MousePointerCrosshairs module supports the following configurable properties:
|
||||
|
||||
### ActivationShortcut
|
||||
|
||||
Sets the keyboard shortcut to toggle crosshairs display.
|
||||
|
||||
**Type:** object
|
||||
**Properties:**
|
||||
|
||||
- `win` (boolean) - Windows key modifier
|
||||
- `ctrl` (boolean) - Ctrl key modifier
|
||||
- `alt` (boolean) - Alt key modifier
|
||||
- `shift` (boolean) - Shift key modifier
|
||||
- `code` (integer) - Virtual key code
|
||||
- `key` (string) - Key name
|
||||
|
||||
**Default:** `Win+Alt+P`
|
||||
|
||||
### CrosshairsColor
|
||||
|
||||
Sets the color of the crosshairs.
|
||||
|
||||
**Type:** string (hex color)
|
||||
**Format:** `"#RRGGBB"`
|
||||
**Default:** `"#FF0000"` (red)
|
||||
|
||||
### CrosshairsOpacity
|
||||
|
||||
Sets the opacity of the crosshairs (0-100).
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `0` to `100`
|
||||
**Default:** `75`
|
||||
|
||||
### CrosshairsRadius
|
||||
|
||||
Sets the length of the crosshair lines in pixels.
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `0` to `9999`
|
||||
**Default:** `100`
|
||||
|
||||
### CrosshairsThickness
|
||||
|
||||
Sets the thickness of the crosshair lines in pixels.
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `1` to `50`
|
||||
**Default:** `5`
|
||||
|
||||
### CrosshairsBorderColor
|
||||
|
||||
Sets the border color of the crosshairs.
|
||||
|
||||
**Type:** string (hex color)
|
||||
**Format:** `"#RRGGBB"`
|
||||
**Default:** `"#FFFFFF"` (white)
|
||||
|
||||
### CrosshairsBorderSize
|
||||
|
||||
Sets the width of the crosshair border in pixels.
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `0` to `50`
|
||||
**Default:** `1`
|
||||
|
||||
### CrosshairsAutoHide
|
||||
|
||||
Controls whether crosshairs automatically hide when the mouse is not moving.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
### CrosshairsIsFixedLengthEnabled
|
||||
|
||||
Controls whether crosshairs have a fixed length or extend to screen edges.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `true`
|
||||
|
||||
### CrosshairsFixedLength
|
||||
|
||||
Sets the fixed length of crosshairs when fixed length mode is enabled.
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `0` to `9999`
|
||||
**Default:** `100`
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Configure crosshair appearance with direct execution
|
||||
|
||||
This example customizes the crosshair color and size.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
CrosshairsColor = "#00FF00"
|
||||
CrosshairsOpacity = 85
|
||||
CrosshairsThickness = 3
|
||||
CrosshairsRadius = 150
|
||||
}
|
||||
name = "MousePointerCrosshairs"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module MousePointerCrosshairs `
|
||||
--input $config
|
||||
```
|
||||
|
||||
### Example 2 - Configure with border with DSC
|
||||
|
||||
This example adds a border to the crosshairs for better visibility.
|
||||
|
||||
```bash
|
||||
dsc config set --file mousecrosshairs-border.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# mousecrosshairs-border.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure crosshairs with border
|
||||
type: Microsoft.PowerToys/MousePointerCrosshairsSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
CrosshairsColor: "#FF0000"
|
||||
CrosshairsBorderColor: "#FFFFFF"
|
||||
CrosshairsBorderSize: 2
|
||||
CrosshairsThickness: 4
|
||||
name: MousePointerCrosshairs
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Install and configure with WinGet
|
||||
|
||||
This example installs PowerToys and configures crosshairs for presentations.
|
||||
|
||||
```bash
|
||||
winget configure winget-mousecrosshairs.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-mousecrosshairs.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Configure Mouse Crosshairs
|
||||
type: Microsoft.PowerToys/MousePointerCrosshairsSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
CrosshairsColor: "#FFFF00"
|
||||
CrosshairsOpacity: 90
|
||||
CrosshairsRadius: 120
|
||||
CrosshairsThickness: 5
|
||||
CrosshairsBorderSize: 2
|
||||
name: MousePointerCrosshairs
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 4 - Full-screen crosshairs
|
||||
|
||||
This example configures crosshairs that extend to screen edges.
|
||||
|
||||
```bash
|
||||
dsc config set --file mousecrosshairs-fullscreen.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# mousecrosshairs-fullscreen.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Full-screen crosshairs
|
||||
type: Microsoft.PowerToys/MousePointerCrosshairsSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
CrosshairsIsFixedLengthEnabled: false
|
||||
CrosshairsOpacity: 60
|
||||
name: MousePointerCrosshairs
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 5 - Subtle crosshairs with auto-hide
|
||||
|
||||
This example creates subtle crosshairs that hide when idle.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
CrosshairsColor = "#FFFFFF"
|
||||
CrosshairsOpacity = 50
|
||||
CrosshairsThickness = 2
|
||||
CrosshairsRadius = 80
|
||||
CrosshairsAutoHide = $true
|
||||
}
|
||||
name = "MousePointerCrosshairs"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module MousePointerCrosshairs --input $config
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### Presentations and demos
|
||||
|
||||
Configure for clear cursor tracking during presentations:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Presentation crosshairs
|
||||
type: Microsoft.PowerToys/MousePointerCrosshairsSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
CrosshairsColor: "#FFFF00"
|
||||
CrosshairsOpacity: 85
|
||||
CrosshairsRadius: 150
|
||||
name: MousePointerCrosshairs
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Design and alignment
|
||||
|
||||
Configure for precise alignment work:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Design crosshairs
|
||||
type: Microsoft.PowerToys/MousePointerCrosshairsSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
CrosshairsIsFixedLengthEnabled: false
|
||||
CrosshairsThickness: 1
|
||||
CrosshairsOpacity: 70
|
||||
name: MousePointerCrosshairs
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [MouseHighlighter][03]
|
||||
- [PowerToys Mouse Utilities Documentation][04]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./MouseHighlighter.md
|
||||
[04]: https://learn.microsoft.com/windows/powertoys/mouse-utilities
|
||||
200
doc/dsc/modules/Peek.md
Normal file
200
doc/dsc/modules/Peek.md
Normal file
@@ -0,0 +1,200 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys Peek module
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: Peek Module
|
||||
---
|
||||
|
||||
# Peek Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration for the Peek utility, a quick file preview tool.
|
||||
|
||||
## Description
|
||||
|
||||
The `Peek` module configures PowerToys Peek, a utility that provides quick
|
||||
file previews without opening files. Activate it with a keyboard shortcut to
|
||||
preview documents, images, videos, and more in a popup window.
|
||||
|
||||
## Properties
|
||||
|
||||
The Peek module supports the following configurable properties:
|
||||
|
||||
### ActivationShortcut
|
||||
|
||||
Sets the keyboard shortcut to activate Peek for the selected file.
|
||||
|
||||
**Type:** object
|
||||
**Properties:**
|
||||
|
||||
- `win` (boolean) - Windows key modifier
|
||||
- `ctrl` (boolean) - Ctrl key modifier
|
||||
- `alt` (boolean) - Alt key modifier
|
||||
- `shift` (boolean) - Shift key modifier
|
||||
- `code` (integer) - Virtual key code
|
||||
- `key` (string) - Key name
|
||||
|
||||
**Default:** `Ctrl+Space`
|
||||
|
||||
### CloseAfterLosingFocus
|
||||
|
||||
Controls whether Peek window closes when it loses focus.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `true`
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Configure activation shortcut with direct execution
|
||||
|
||||
This example customizes the Peek activation shortcut.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
ActivationShortcut = @{
|
||||
win = $false
|
||||
ctrl = $true
|
||||
alt = $false
|
||||
shift = $false
|
||||
code = 32
|
||||
key = "Space"
|
||||
}
|
||||
}
|
||||
name = "Peek"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module Peek --input $config
|
||||
```
|
||||
|
||||
### Example 2 - Configure focus behavior with DSC
|
||||
|
||||
This example configures Peek to remain open after losing focus.
|
||||
|
||||
```bash
|
||||
dsc config set --file peek-focus.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# peek-focus.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure Peek focus behavior
|
||||
type: Microsoft.PowerToys/PeekSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
CloseAfterLosingFocus: false
|
||||
name: Peek
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Install and configure with WinGet
|
||||
|
||||
This example installs PowerToys and configures Peek.
|
||||
|
||||
```bash
|
||||
winget configure winget-peek.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-peek.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Configure Peek
|
||||
type: Microsoft.PowerToys/PeekSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
CloseAfterLosingFocus: true
|
||||
name: Peek
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 4 - Alternative activation shortcut
|
||||
|
||||
This example uses Ctrl+Shift+Space as the activation shortcut.
|
||||
|
||||
```bash
|
||||
dsc config set --file peek-altkey.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# peek-altkey.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Alternative Peek shortcut
|
||||
type: Microsoft.PowerToys/PeekSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ActivationShortcut:
|
||||
win: false
|
||||
ctrl: true
|
||||
alt: false
|
||||
shift: true
|
||||
code: 32
|
||||
key: Space
|
||||
name: Peek
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### File browsing
|
||||
|
||||
Configure for quick file preview during browsing:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: File browsing configuration
|
||||
type: Microsoft.PowerToys/PeekSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
CloseAfterLosingFocus: true
|
||||
name: Peek
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Content review
|
||||
|
||||
Configure for extended content review:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Review configuration
|
||||
type: Microsoft.PowerToys/PeekSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
CloseAfterLosingFocus: false
|
||||
name: Peek
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [ShortcutGuide][03]
|
||||
- [PowerToys Peek Documentation][04]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./ShortcutGuide.md
|
||||
[04]: https://learn.microsoft.com/windows/powertoys/peek
|
||||
257
doc/dsc/modules/PowerAccent.md
Normal file
257
doc/dsc/modules/PowerAccent.md
Normal file
@@ -0,0 +1,257 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys PowerAccent module
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: PowerAccent Module
|
||||
---
|
||||
|
||||
# PowerAccent Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration for the Power Accent utility, a quick accent character selector.
|
||||
|
||||
## Description
|
||||
|
||||
The `PowerAccent` module configures PowerToys Power Accent (Quick Accent), a
|
||||
utility that provides quick access to accented characters. Hold down a key
|
||||
and use arrow keys or numbers to select from available accent variations.
|
||||
|
||||
## Properties
|
||||
|
||||
The PowerAccent module supports the following configurable properties:
|
||||
|
||||
### ActivationKey
|
||||
|
||||
Sets which key triggers the accent selection.
|
||||
|
||||
**Type:** string
|
||||
**Allowed values:**
|
||||
|
||||
- `"LeftRightArrow"` - Hold left or right arrow keys
|
||||
- `"Space"` - Hold spacebar
|
||||
- `"Both"` - Hold either left/right arrows or spacebar
|
||||
|
||||
**Default:** `"Both"`
|
||||
|
||||
### InputTime
|
||||
|
||||
Sets how long the activation key must be held (in milliseconds) before showing accents.
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `100` to `1000`
|
||||
**Default:** `300`
|
||||
|
||||
### ExcludedApps
|
||||
|
||||
List of applications where Power Accent is disabled.
|
||||
|
||||
**Type:** string (newline-separated list of executable names)
|
||||
|
||||
### ToolbarPosition
|
||||
|
||||
Sets the position of the accent selection toolbar.
|
||||
|
||||
**Type:** string
|
||||
**Allowed values:**
|
||||
|
||||
- `"Top"` - Above the cursor
|
||||
- `"Bottom"` - Below the cursor
|
||||
- `"Left"` - To the left of cursor
|
||||
- `"Right"` - To the right of cursor
|
||||
- `"Center"` - Centered on screen
|
||||
|
||||
**Default:** `"Top"`
|
||||
|
||||
### ShowUnicodeDescription
|
||||
|
||||
Controls whether Unicode descriptions are shown for each accent character.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
### SortByUsageFrequency
|
||||
|
||||
Controls whether accent characters are sorted by usage frequency.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
### StartSelectionFromTheLeft
|
||||
|
||||
Controls whether selection starts from the left side.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Configure activation method with direct execution
|
||||
|
||||
This example sets spacebar as the activation key.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
ActivationKey = "Space"
|
||||
InputTime = 250
|
||||
}
|
||||
name = "PowerAccent"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module PowerAccent `
|
||||
--input $config
|
||||
```
|
||||
|
||||
### Example 2 - Configure toolbar appearance with DSC
|
||||
|
||||
This example customizes the toolbar position and display options.
|
||||
|
||||
```bash
|
||||
dsc config set --file poweraccent-toolbar.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# poweraccent-toolbar.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure Power Accent toolbar
|
||||
type: Microsoft.PowerToys/PowerAccentSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ToolbarPosition: Bottom
|
||||
ShowUnicodeDescription: true
|
||||
SortByUsageFrequency: true
|
||||
name: PowerAccent
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Install and configure with WinGet
|
||||
|
||||
This example installs PowerToys and configures Power Accent for multilingual
|
||||
typing.
|
||||
|
||||
```bash
|
||||
winget configure winget-poweraccent.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-poweraccent.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Configure Power Accent
|
||||
type: Microsoft.PowerToys/PowerAccentSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ActivationKey: Space
|
||||
InputTime: 300
|
||||
ToolbarPosition: Top
|
||||
SortByUsageFrequency: true
|
||||
name: PowerAccent
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 4 - Fast activation configuration
|
||||
|
||||
This example configures for quick accent selection.
|
||||
|
||||
```bash
|
||||
dsc config set --file poweraccent-fast.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# poweraccent-fast.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Fast accent activation
|
||||
type: Microsoft.PowerToys/PowerAccentSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
InputTime: 150
|
||||
SortByUsageFrequency: true
|
||||
name: PowerAccent
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 5 - Exclude applications
|
||||
|
||||
This example excludes specific applications from Power Accent.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
ExcludedApps = "notepad.exe`nWordPad.exe"
|
||||
}
|
||||
name = "PowerAccent"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module PowerAccent --input $config
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### Multilingual content creation
|
||||
|
||||
Configure for efficient multilingual typing:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Multilingual configuration
|
||||
type: Microsoft.PowerToys/PowerAccentSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ActivationKey: Space
|
||||
SortByUsageFrequency: true
|
||||
ShowUnicodeDescription: false
|
||||
name: PowerAccent
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Language learning
|
||||
|
||||
Configure for language learning with Unicode descriptions:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Learning configuration
|
||||
type: Microsoft.PowerToys/PowerAccentSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ShowUnicodeDescription: true
|
||||
InputTime: 400
|
||||
name: PowerAccent
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [Workspaces][03]
|
||||
- [PowerToys Quick Accent Documentation][04]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./Workspaces.md
|
||||
[04]: https://learn.microsoft.com/windows/powertoys/quick-accent
|
||||
197
doc/dsc/modules/PowerOCR.md
Normal file
197
doc/dsc/modules/PowerOCR.md
Normal file
@@ -0,0 +1,197 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys PowerOCR module
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: PowerOCR Module
|
||||
---
|
||||
|
||||
# PowerOCR Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration for the Power OCR (Text Extractor) utility, which
|
||||
extracts text from images and screen regions.
|
||||
|
||||
## Description
|
||||
|
||||
The `PowerOCR` module configures PowerToys Power OCR (Text Extractor), a
|
||||
utility that uses optical character recognition (OCR) to extract text from
|
||||
any screen region and copy it to the clipboard. It's useful for capturing
|
||||
text from images, videos, PDFs, or any on-screen content.
|
||||
|
||||
## Properties
|
||||
|
||||
The PowerOCR module supports the following configurable properties:
|
||||
|
||||
### ActivationShortcut
|
||||
|
||||
Sets the keyboard shortcut to activate text extraction.
|
||||
|
||||
**Type:** object
|
||||
**Properties:**
|
||||
|
||||
- `win` (boolean) - Windows key modifier
|
||||
- `ctrl` (boolean) - Ctrl key modifier
|
||||
- `alt` (boolean) - Alt key modifier
|
||||
- `shift` (boolean) - Shift key modifier
|
||||
- `code` (integer) - Virtual key code
|
||||
- `key` (string) - Key name
|
||||
|
||||
**Default:** `Win+Shift+T`
|
||||
|
||||
### PreferredLanguage
|
||||
|
||||
Sets the preferred language for OCR recognition.
|
||||
|
||||
**Type:** string
|
||||
**Default:** System language
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Configure activation shortcut with direct execution
|
||||
|
||||
This example customizes the OCR activation shortcut.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
ActivationShortcut = @{
|
||||
win = $true
|
||||
ctrl = $false
|
||||
alt = $false
|
||||
shift = $true
|
||||
code = 84
|
||||
key = "T"
|
||||
}
|
||||
}
|
||||
name = "PowerOCR"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module PowerOCR `
|
||||
--input $config
|
||||
```
|
||||
|
||||
### Example 2 - Configure language with DSC
|
||||
|
||||
This example sets the preferred OCR language.
|
||||
|
||||
```bash
|
||||
dsc config set --file powerocr-language.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# powerocr-language.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure Power OCR language
|
||||
type: Microsoft.PowerToys/PowerOCRSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
PreferredLanguage: en-US
|
||||
name: PowerOCR
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Install and configure with WinGet
|
||||
|
||||
This example installs PowerToys and configures Power OCR.
|
||||
|
||||
```bash
|
||||
winget configure winget-powerocr.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-powerocr.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Configure Power OCR
|
||||
type: Microsoft.PowerToys/PowerOCRSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
PreferredLanguage: en-US
|
||||
name: PowerOCR
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 4 - Multilingual configuration
|
||||
|
||||
This example configures for multilingual text extraction.
|
||||
|
||||
```bash
|
||||
dsc config set --file powerocr-multilingual.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# powerocr-multilingual.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Multilingual OCR
|
||||
type: Microsoft.PowerToys/PowerOCRSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
PreferredLanguage: fr-FR
|
||||
name: PowerOCR
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### Document digitization
|
||||
|
||||
Configure for extracting text from documents:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Document OCR
|
||||
type: Microsoft.PowerToys/PowerOCRSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
PreferredLanguage: en-US
|
||||
name: PowerOCR
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### International content
|
||||
|
||||
Configure for multilingual content extraction:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Multilingual OCR
|
||||
type: Microsoft.PowerToys/PowerOCRSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
PreferredLanguage: es-ES
|
||||
name: PowerOCR
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [ZoomIt][03]
|
||||
- [PowerToys Text Extractor Documentation][04]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./ZoomIt.md
|
||||
[04]: https://learn.microsoft.com/windows/powertoys/text-extractor
|
||||
230
doc/dsc/modules/PowerRename.md
Normal file
230
doc/dsc/modules/PowerRename.md
Normal file
@@ -0,0 +1,230 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys PowerRename module
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: PowerRename Module
|
||||
---
|
||||
|
||||
# PowerRename Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration for the Power Rename utility, a bulk file and folder renaming tool.
|
||||
|
||||
## Description
|
||||
|
||||
The `PowerRename` module configures PowerToys Power Rename, a Windows shell
|
||||
extension that enables bulk renaming of files and folders with advanced
|
||||
features like regular expressions, preview, and undo functionality. It
|
||||
integrates with the Windows Explorer context menu.
|
||||
|
||||
## Properties
|
||||
|
||||
The PowerRename module supports the following configurable properties:
|
||||
|
||||
### MRUEnabled
|
||||
|
||||
Controls whether the most recently used (MRU) search and replace terms are saved.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `true`
|
||||
|
||||
### MaxMRUSize
|
||||
|
||||
Sets the maximum number of MRU entries to remember.
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `0` to `20`
|
||||
**Default:** `10`
|
||||
|
||||
### ShowIcon
|
||||
|
||||
Controls whether the Power Rename icon appears in the Explorer context menu.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `true`
|
||||
|
||||
### ExtendedContextMenuOnly
|
||||
|
||||
Controls whether Power Rename appears only in the extended context menu (Shift+right-click).
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
### UseBoostLib
|
||||
|
||||
Controls whether the Boost library is used for regular expression processing.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Configure MRU settings with direct execution
|
||||
|
||||
This example configures the most recently used list behavior.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
MRUEnabled = $true
|
||||
MaxMRUSize = 15
|
||||
}
|
||||
name = "PowerRename"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module PowerRename --input $config
|
||||
```
|
||||
|
||||
### Example 2 - Configure context menu with DSC
|
||||
|
||||
This example configures Power Rename to appear in the extended context menu
|
||||
only.
|
||||
|
||||
```bash
|
||||
dsc config set --file powerrename-context.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# powerrename-context.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure Power Rename context menu
|
||||
type: Microsoft.PowerToys/PowerRenameSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ExtendedContextMenuOnly: true
|
||||
ShowIcon: true
|
||||
name: PowerRename
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Install and configure with WinGet
|
||||
|
||||
This example installs PowerToys and configures Power Rename.
|
||||
|
||||
```bash
|
||||
winget configure winget-powerrename.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-powerrename.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Configure Power Rename
|
||||
type: Microsoft.PowerToys/PowerRenameSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
MRUEnabled: true
|
||||
MaxMRUSize: 20
|
||||
ShowIcon: true
|
||||
UseBoostLib: true
|
||||
name: PowerRename
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 4 - Clean context menu configuration
|
||||
|
||||
This example minimizes context menu clutter.
|
||||
|
||||
```bash
|
||||
dsc config set --file powerrename-minimal.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# powerrename-minimal.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Minimal context menu
|
||||
type: Microsoft.PowerToys/PowerRenameSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ExtendedContextMenuOnly: true
|
||||
ShowIcon: false
|
||||
name: PowerRename
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 5 - Advanced regex configuration
|
||||
|
||||
This example enables the Boost library for advanced regex features.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
UseBoostLib = $true
|
||||
MRUEnabled = $true
|
||||
MaxMRUSize = 15
|
||||
}
|
||||
name = "PowerRename"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module PowerRename --input $config
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### Content management
|
||||
|
||||
Configure for frequent file renaming tasks:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Content management
|
||||
type: Microsoft.PowerToys/PowerRenameSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
MRUEnabled: true
|
||||
MaxMRUSize: 20
|
||||
ShowIcon: true
|
||||
name: PowerRename
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Clean interface
|
||||
|
||||
Configure for minimal context menu presence:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Clean interface
|
||||
type: Microsoft.PowerToys/PowerRenameSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ExtendedContextMenuOnly: true
|
||||
name: PowerRename
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [AdvancedPaste][03]
|
||||
- [PowerToys PowerRename Documentation][04]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./AdvancedPaste.md
|
||||
[04]: https://learn.microsoft.com/windows/powertoys/powerrename
|
||||
173
doc/dsc/modules/RegistryPreview.md
Normal file
173
doc/dsc/modules/RegistryPreview.md
Normal file
@@ -0,0 +1,173 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys RegistryPreview module
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: RegistryPreview Module
|
||||
---
|
||||
|
||||
# RegistryPreview Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration for the Registry Preview utility, which visualizes and edits Windows registry files (.reg).
|
||||
|
||||
## Description
|
||||
|
||||
The `RegistryPreview` module configures PowerToys Registry Preview, a utility
|
||||
that provides a visual preview and editing interface for Windows registry
|
||||
(.reg) files. It helps you understand and safely edit registry files before
|
||||
applying them to your system.
|
||||
|
||||
## Properties
|
||||
|
||||
The RegistryPreview module supports the following configurable properties:
|
||||
|
||||
### DefaultRegApp
|
||||
|
||||
Controls whether Registry Preview is set as the default application for .reg files.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Set as default .reg handler with direct execution
|
||||
|
||||
This example sets Registry Preview as the default application for .reg files.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
DefaultRegApp = $true
|
||||
}
|
||||
name = "RegistryPreview"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module RegistryPreview --input $config
|
||||
```
|
||||
|
||||
### Example 2 - Configure with DSC
|
||||
|
||||
This example configures Registry Preview as the default handler.
|
||||
|
||||
```bash
|
||||
dsc config set --file registrypreview-default.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# registrypreview-default.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Set Registry Preview as default
|
||||
type: Microsoft.PowerToys/RegistryPreviewSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
DefaultRegApp: true
|
||||
name: RegistryPreview
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Install and configure with WinGet
|
||||
|
||||
This example installs PowerToys and sets Registry Preview as the default .reg
|
||||
handler.
|
||||
|
||||
```bash
|
||||
winget configure winget-registrypreview.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-registrypreview.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Configure Registry Preview
|
||||
type: Microsoft.PowerToys/RegistryPreviewSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
DefaultRegApp: true
|
||||
name: RegistryPreview
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 4 - Disable as default handler
|
||||
|
||||
This example ensures Registry Preview is not the default .reg handler.
|
||||
|
||||
```bash
|
||||
dsc config set --file registrypreview-notdefault.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# registrypreview-notdefault.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Do not use as default
|
||||
type: Microsoft.PowerToys/RegistryPreviewSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
DefaultRegApp: false
|
||||
name: RegistryPreview
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### System administration
|
||||
|
||||
Configure as default for safe registry file handling:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Admin configuration
|
||||
type: Microsoft.PowerToys/RegistryPreviewSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
DefaultRegApp: true
|
||||
name: RegistryPreview
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Optional tool
|
||||
|
||||
Keep as optional tool without default file association:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Optional tool
|
||||
type: Microsoft.PowerToys/RegistryPreviewSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
DefaultRegApp: false
|
||||
name: RegistryPreview
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [FileLocksmith][03]
|
||||
- [PowerToys Registry Preview Documentation][04]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./FileLocksmith.md
|
||||
[04]: https://learn.microsoft.com/windows/powertoys/registry-preview
|
||||
259
doc/dsc/modules/ShortcutGuide.md
Normal file
259
doc/dsc/modules/ShortcutGuide.md
Normal file
@@ -0,0 +1,259 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys ShortcutGuide module
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: ShortcutGuide Module
|
||||
---
|
||||
|
||||
# ShortcutGuide Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration for the Shortcut Guide utility, which displays available keyboard shortcuts.
|
||||
|
||||
## Description
|
||||
|
||||
The `ShortcutGuide` module configures PowerToys Shortcut Guide, a utility that
|
||||
displays an overlay showing available Windows keyboard shortcuts when you hold
|
||||
the Windows key. It helps users discover and learn keyboard shortcuts.
|
||||
|
||||
## Properties
|
||||
|
||||
The ShortcutGuide module supports the following configurable properties:
|
||||
|
||||
### OpenShortcutGuide
|
||||
|
||||
Sets the keyboard shortcut or method to open the shortcut guide.
|
||||
|
||||
**Type:** object
|
||||
**Properties:**
|
||||
|
||||
- `win` (boolean) - Windows key modifier
|
||||
- `ctrl` (boolean) - Ctrl key modifier
|
||||
- `alt` (boolean) - Alt key modifier
|
||||
- `shift` (boolean) - Shift key modifier
|
||||
- `code` (integer) - Virtual key code
|
||||
- `key` (string) - Key name
|
||||
|
||||
**Default:** Hold Windows key for 900ms
|
||||
|
||||
### OverlayOpacity
|
||||
|
||||
Sets the opacity of the shortcut guide overlay (0-100).
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `0` to `100`
|
||||
**Default:** `90`
|
||||
|
||||
### Theme
|
||||
|
||||
Sets the theme for the shortcut guide.
|
||||
|
||||
**Type:** string
|
||||
**Allowed values:** `"light"`, `"dark"`, `"system"`
|
||||
**Default:** `"dark"`
|
||||
|
||||
### PressTime
|
||||
|
||||
Sets how long the Windows key must be held before showing the guide (in milliseconds).
|
||||
|
||||
**Type:** integer
|
||||
**Range:** `100` to `10000`
|
||||
**Default:** `900`
|
||||
|
||||
### ExcludedApps
|
||||
|
||||
List of applications where Shortcut Guide is disabled.
|
||||
|
||||
**Type:** string (newline-separated list of executable names)
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Configure activation time with direct execution
|
||||
|
||||
This example sets a faster activation time for the shortcut guide.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
PressTime = 600
|
||||
}
|
||||
name = "ShortcutGuide"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module ShortcutGuide `
|
||||
--input $config
|
||||
```
|
||||
|
||||
### Example 2 - Configure appearance with DSC
|
||||
|
||||
This example customizes the overlay appearance.
|
||||
|
||||
```bash
|
||||
dsc config set --file shortcutguide-appearance.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# shortcutguide-appearance.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure Shortcut Guide appearance
|
||||
type: Microsoft.PowerToys/ShortcutGuideSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
OverlayOpacity: 95
|
||||
Theme: light
|
||||
name: ShortcutGuide
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Install and configure with WinGet
|
||||
|
||||
This example installs PowerToys and configures Shortcut Guide.
|
||||
|
||||
```bash
|
||||
winget configure winget-shortcutguide.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-shortcutguide.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Configure Shortcut Guide
|
||||
type: Microsoft.PowerToys/ShortcutGuideSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
PressTime: 700
|
||||
OverlayOpacity: 90
|
||||
Theme: dark
|
||||
name: ShortcutGuide
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 4 - Quick activation
|
||||
|
||||
This example configures for quick activation with a short press time.
|
||||
|
||||
```bash
|
||||
dsc config set --file shortcutguide-quick.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# shortcutguide-quick.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Quick activation
|
||||
type: Microsoft.PowerToys/ShortcutGuideSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
PressTime: 400
|
||||
name: ShortcutGuide
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 5 - High opacity for visibility
|
||||
|
||||
This example maximizes opacity for better visibility.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
OverlayOpacity = 100
|
||||
Theme = "dark"
|
||||
}
|
||||
name = "ShortcutGuide"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module ShortcutGuide --input $config
|
||||
```
|
||||
|
||||
### Example 6 - Exclude applications
|
||||
|
||||
This example excludes Shortcut Guide from specific applications.
|
||||
|
||||
```bash
|
||||
dsc config set --file shortcutguide-exclusions.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# shortcutguide-exclusions.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Exclude apps
|
||||
type: Microsoft.PowerToys/ShortcutGuideSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ExcludedApps: |
|
||||
Game.exe
|
||||
FullScreenApp.exe
|
||||
name: ShortcutGuide
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### New users
|
||||
|
||||
Configure for easy keyboard shortcut discovery:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: New user configuration
|
||||
type: Microsoft.PowerToys/ShortcutGuideSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
PressTime: 800
|
||||
OverlayOpacity: 95
|
||||
name: ShortcutGuide
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Power users
|
||||
|
||||
Configure for quick access without accidental activation:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Power user configuration
|
||||
type: Microsoft.PowerToys/ShortcutGuideSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
PressTime: 1200
|
||||
OverlayOpacity: 85
|
||||
name: ShortcutGuide
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [Peek][03]
|
||||
- [PowerToys Keyboard Shortcut Guide Documentation][04]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./Peek.md
|
||||
[04]: https://learn.microsoft.com/windows/powertoys/shortcut-guide
|
||||
238
doc/dsc/modules/Workspaces.md
Normal file
238
doc/dsc/modules/Workspaces.md
Normal file
@@ -0,0 +1,238 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys Workspaces module
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: Workspaces Module
|
||||
---
|
||||
|
||||
# Workspaces Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration for the Workspaces utility, which launches application sets and arranges windows.
|
||||
|
||||
## Description
|
||||
|
||||
The `Workspaces` module configures PowerToys Workspaces, a utility that allows
|
||||
you to save and restore sets of applications with their window positions. It
|
||||
enables you to quickly switch between different work contexts by launching and
|
||||
arranging multiple applications at once.
|
||||
|
||||
## Properties
|
||||
|
||||
The Workspaces module supports the following configurable properties:
|
||||
|
||||
### LaunchHotkey
|
||||
|
||||
Sets the keyboard shortcut to launch the Workspaces editor.
|
||||
|
||||
**Type:** object
|
||||
**Properties:**
|
||||
|
||||
- `win` (boolean) - Windows key modifier
|
||||
- `ctrl` (boolean) - Ctrl key modifier
|
||||
- `alt` (boolean) - Alt key modifier
|
||||
- `shift` (boolean) - Shift key modifier
|
||||
- `code` (integer) - Virtual key code
|
||||
- `key` (string) - Key name
|
||||
|
||||
**Default:** `Win+Shift+;` (VK code 186)
|
||||
|
||||
### MoveExistingWindows
|
||||
|
||||
Controls whether existing application windows are moved when launching a workspace.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
### SpanZonesAcrossMonitors
|
||||
|
||||
Controls whether workspace zones can span across multiple monitors.
|
||||
|
||||
**Type:** boolean
|
||||
**Default:** `false`
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Configure launch hotkey with direct execution
|
||||
|
||||
This example sets a custom hotkey to launch the Workspaces editor.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
LaunchHotkey = @{
|
||||
win = $true
|
||||
ctrl = $true
|
||||
alt = $false
|
||||
shift = $false
|
||||
code = 87
|
||||
key = "W"
|
||||
}
|
||||
}
|
||||
name = "Workspaces"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module Workspaces --input $config
|
||||
```
|
||||
|
||||
### Example 2 - Configure window behavior with DSC
|
||||
|
||||
This example enables moving existing windows when launching workspaces.
|
||||
|
||||
```bash
|
||||
dsc config set --file workspaces-behavior.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# workspaces-behavior.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure Workspaces window behavior
|
||||
type: Microsoft.PowerToys/WorkspacesSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
MoveExistingWindows: true
|
||||
SpanZonesAcrossMonitors: false
|
||||
name: Workspaces
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Install and configure with WinGet
|
||||
|
||||
This example installs PowerToys and configures Workspaces.
|
||||
|
||||
```bash
|
||||
winget configure winget-workspaces.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-workspaces.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Configure Workspaces
|
||||
type: Microsoft.PowerToys/WorkspacesSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
LaunchHotkey:
|
||||
win: true
|
||||
ctrl: false
|
||||
alt: false
|
||||
shift: true
|
||||
code: 186
|
||||
key: ";"
|
||||
MoveExistingWindows: true
|
||||
name: Workspaces
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 4 - Multi-monitor setup
|
||||
|
||||
This example configures for multi-monitor workspace management.
|
||||
|
||||
```bash
|
||||
dsc config set --file workspaces-multimonitor.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# workspaces-multimonitor.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Multi-monitor configuration
|
||||
type: Microsoft.PowerToys/WorkspacesSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
SpanZonesAcrossMonitors: true
|
||||
MoveExistingWindows: true
|
||||
name: Workspaces
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 5 - Simple hotkey
|
||||
|
||||
This example sets a simple single-key hotkey combination.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
LaunchHotkey = @{
|
||||
win = $true
|
||||
ctrl = $false
|
||||
alt = $true
|
||||
shift = $false
|
||||
code = 192
|
||||
key = "~"
|
||||
}
|
||||
}
|
||||
name = "Workspaces"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module Workspaces --input $config
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### Development environments
|
||||
|
||||
Configure for quick switching between development workspaces:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Development workspace
|
||||
type: Microsoft.PowerToys/WorkspacesSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
MoveExistingWindows: true
|
||||
SpanZonesAcrossMonitors: true
|
||||
name: Workspaces
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Single monitor usage
|
||||
|
||||
Configure for single-monitor workflow:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Single monitor setup
|
||||
type: Microsoft.PowerToys/WorkspacesSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
SpanZonesAcrossMonitors: false
|
||||
MoveExistingWindows: false
|
||||
name: Workspaces
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [ColorPicker Module][03] - For additional PowerToys configuration
|
||||
- [PowerToys Workspaces Documentation][04]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./ColorPicker.md
|
||||
[04]: https://learn.microsoft.com/windows/powertoys/workspaces
|
||||
215
doc/dsc/modules/ZoomIt.md
Normal file
215
doc/dsc/modules/ZoomIt.md
Normal file
@@ -0,0 +1,215 @@
|
||||
---
|
||||
description: DSC configuration reference for PowerToys ZoomIt module
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: ZoomIt Module
|
||||
---
|
||||
|
||||
# ZoomIt Module
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration for the ZoomIt utility, which provides screen zoom, annotation, and presentation tools.
|
||||
|
||||
## Description
|
||||
|
||||
The `ZoomIt` module configures PowerToys ZoomIt, a screen zoom and annotation utility for presentations and demonstrations. It provides live zoom, screen drawing, a break timer, and other presentation features activated through customizable keyboard shortcuts.
|
||||
|
||||
## Properties
|
||||
|
||||
The ZoomIt module supports the following configurable properties:
|
||||
|
||||
### ActivationShortcut
|
||||
|
||||
Sets the keyboard shortcut to activate the zoom mode.
|
||||
|
||||
**Type:** object
|
||||
**Properties:**
|
||||
|
||||
- `win` (boolean) - Windows key modifier
|
||||
- `ctrl` (boolean) - Ctrl key modifier
|
||||
- `alt` (boolean) - Alt key modifier
|
||||
- `shift` (boolean) - Shift key modifier
|
||||
- `code` (integer) - Virtual key code
|
||||
- `key` (string) - Key name
|
||||
|
||||
**Default:** `Ctrl+1` (VK code 49)
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Configure activation shortcut with direct execution
|
||||
|
||||
This example sets a custom keyboard shortcut to activate ZoomIt.
|
||||
|
||||
```powershell
|
||||
$config = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
ActivationShortcut = @{
|
||||
win = $false
|
||||
ctrl = $true
|
||||
alt = $false
|
||||
shift = $true
|
||||
code = 90
|
||||
key = "Z"
|
||||
}
|
||||
}
|
||||
name = "ZoomIt"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
PowerToys.DSC.exe set --resource 'settings' --module ZoomIt --input $config
|
||||
```
|
||||
|
||||
### Example 2 - Configure with Microsoft DSC
|
||||
|
||||
This example configures the ZoomIt activation shortcut using Microsoft DSC.
|
||||
|
||||
```bash
|
||||
dsc config set --file zoomit-config.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# zoomit-config.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure ZoomIt shortcut
|
||||
type: Microsoft.PowerToys/ZoomItSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ActivationShortcut:
|
||||
win: false
|
||||
ctrl: true
|
||||
alt: false
|
||||
shift: false
|
||||
code: 49
|
||||
key: "1"
|
||||
name: ZoomIt
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Install and configure with WinGet
|
||||
|
||||
This example installs PowerToys and configures ZoomIt using WinGet.
|
||||
|
||||
```bash
|
||||
winget configure winget-zoomit.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# winget-zoomit.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Configure ZoomIt
|
||||
type: Microsoft.PowerToys/ZoomItSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ActivationShortcut:
|
||||
win: false
|
||||
ctrl: true
|
||||
alt: false
|
||||
shift: true
|
||||
code: 90
|
||||
key: Z
|
||||
name: ZoomIt
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 4 - Presentation mode hotkey
|
||||
|
||||
This example configures an easy-to-remember presentation hotkey.
|
||||
|
||||
```bash
|
||||
dsc config set --file zoomit-presentation.dsc.yaml
|
||||
```
|
||||
|
||||
```yaml
|
||||
# zoomit-presentation.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Presentation hotkey
|
||||
type: Microsoft.PowerToys/ZoomItSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ActivationShortcut:
|
||||
win: true
|
||||
ctrl: false
|
||||
alt: false
|
||||
shift: false
|
||||
code: 187
|
||||
key: "="
|
||||
name: ZoomIt
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## Use cases
|
||||
|
||||
### Presentations
|
||||
|
||||
Configure for easy screen zooming during presentations:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Presentation setup
|
||||
type: Microsoft.PowerToys/ZoomItSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ActivationShortcut:
|
||||
win: false
|
||||
ctrl: true
|
||||
alt: false
|
||||
shift: false
|
||||
code: 49
|
||||
key: "1"
|
||||
name: ZoomIt
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Screen recording
|
||||
|
||||
Configure for quick access during screen recording sessions:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- name: Recording setup
|
||||
type: Microsoft.PowerToys/ZoomItSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ActivationShortcut:
|
||||
win: true
|
||||
ctrl: false
|
||||
alt: false
|
||||
shift: true
|
||||
code: 90
|
||||
key: Z
|
||||
name: ZoomIt
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource][01]
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [CropAndLock Module][03] - For additional PowerToys configuration
|
||||
- [PowerToys ZoomIt Documentation][04]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ../settings-resource.md
|
||||
[02]: ../overview.md
|
||||
[03]: ./CropAndLock.md
|
||||
[04]: https://learn.microsoft.com/windows/powertoys/zoomit
|
||||
244
doc/dsc/overview.md
Normal file
244
doc/dsc/overview.md
Normal file
@@ -0,0 +1,244 @@
|
||||
---
|
||||
description: Overview of PowerToys Desired State Configuration (DSC) support
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: overview
|
||||
title: PowerToys DSC Overview
|
||||
---
|
||||
|
||||
# PowerToys DSC Overview
|
||||
|
||||
## Synopsis
|
||||
|
||||
PowerToys supports Desired State Configuration (DSC) v3 for declarative configuration management of PowerToys settings.
|
||||
|
||||
## Description
|
||||
|
||||
PowerToys includes Microsoft Desired State Configuration (DSC) support
|
||||
through the `PowerToys.DSC.exe` command-line tool, enabling you to:
|
||||
|
||||
- Declare and enforce desired configuration states for PowerToys
|
||||
utilities.
|
||||
- Automate PowerToys configuration across multiple systems.
|
||||
- Integrate PowerToys configuration with WinGet and other DSC-compatible
|
||||
tools.
|
||||
- Version control your PowerToys settings as code.
|
||||
|
||||
The PowerToys DSC implementation provides a **settings** resource that
|
||||
manages configuration for all PowerToys utilities (modules). Each utility
|
||||
can be configured independently, allowing granular control over your
|
||||
PowerToys environment.
|
||||
|
||||
## Usage methods
|
||||
|
||||
PowerToys DSC can be used in three ways:
|
||||
|
||||
### 1. Direct execution with PowerToys.DSC.exe
|
||||
|
||||
Execute DSC operations directly using the PowerToys.DSC.exe command-line
|
||||
tool:
|
||||
|
||||
```powershell
|
||||
# Get current settings for a module
|
||||
PowerToys.DSC.exe get --resource 'settings' --module Awake
|
||||
|
||||
# Set settings for a module
|
||||
$input = '{"settings":{...}}'
|
||||
PowerToys.DSC.exe set --resource 'settings' --module Awake --input $input
|
||||
|
||||
# Test if settings match desired state
|
||||
PowerToys.DSC.exe test --resource 'settings' --module Awake --input $input
|
||||
```
|
||||
|
||||
For detailed information, see [PowerToys.DSC.exe command reference][01].
|
||||
|
||||
### 2. Microsoft Desired State Configuration (DSC)
|
||||
|
||||
Use PowerToys DSC resources in standard DSC configuration documents:
|
||||
|
||||
```yaml
|
||||
# powertoys-config.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure Awake
|
||||
type: Microsoft.PowerToys/AwakeSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
keepDisplayOn: true
|
||||
mode: 1
|
||||
name: Awake
|
||||
version: 0.0.1
|
||||
```
|
||||
|
||||
### 3. WinGet Configuration
|
||||
|
||||
Integrate PowerToys configuration with WinGet package installation:
|
||||
|
||||
```yaml
|
||||
# winget-powertoys.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Configure FancyZones
|
||||
type: Microsoft.PowerToys/FancyZonesSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
fancyzones_shiftDrag: true
|
||||
fancyzones_mouseSwitch: true
|
||||
name: FancyZones
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
## Available resources
|
||||
|
||||
PowerToys DSC provides the following resource:
|
||||
|
||||
| Resource | Description |
|
||||
| ---------- | ---------------------------------------------------- |
|
||||
| `settings` | Manages configuration for PowerToys utility modules. |
|
||||
|
||||
For detailed information about the settings resource, see [Settings
|
||||
Resource Reference][03].
|
||||
|
||||
## Available modules
|
||||
|
||||
The settings resource supports configuration for the following PowerToys
|
||||
utilities:
|
||||
|
||||
| Module | Description | Documentation |
|
||||
| ---------------------- | -------------------------------------------- | ------------------------------------- |
|
||||
| App | General PowerToys application settings. | [App module][04] |
|
||||
| AdvancedPaste | Advanced clipboard operations. | [AdvancedPaste module][05] |
|
||||
| AlwaysOnTop | Pin windows to stay on top. | [AlwaysOnTop module][06] |
|
||||
| Awake | Keep computer awake. | [Awake module][07] |
|
||||
| ColorPicker | System-wide color picker utility. | [ColorPicker module][08] |
|
||||
| CropAndLock | Crop and lock portions of windows. | [CropAndLock module][09] |
|
||||
| EnvironmentVariables | Manage environment variables. | [EnvironmentVariables module][10] |
|
||||
| FancyZones | Window layout manager. | [FancyZones module][11] |
|
||||
| FileLocksmith | Identify what's locking files. | [FileLocksmith module][12] |
|
||||
| FindMyMouse | Locate your mouse cursor. | [FindMyMouse module][13] |
|
||||
| Hosts | Quick hosts file editor. | [Hosts module][14] |
|
||||
| ImageResizer | Resize images from context menu. | [ImageResizer module][15] |
|
||||
| KeyboardManager | Remap keys and create shortcuts. | [KeyboardManager module][16] |
|
||||
| MeasureTool | Measure pixels on screen. | [MeasureTool module][17] |
|
||||
| MouseHighlighter | Highlight mouse cursor. | [MouseHighlighter module][18] |
|
||||
| MouseJump | Jump across large or multiple displays. | [MouseJump module][19] |
|
||||
| MousePointerCrosshairs | Display crosshairs centered on mouse. | [MousePointerCrosshairs module][20] |
|
||||
| Peek | Quick file previewer. | [Peek module][21] |
|
||||
| PowerAccent | Quick accent character selector. | [PowerAccent module][22] |
|
||||
| PowerOCR | Extract text from images. | [PowerOCR module][23] |
|
||||
| PowerRename | Bulk rename files. | [PowerRename module][24] |
|
||||
| RegistryPreview | Visualize and edit registry files. | [RegistryPreview module][25] |
|
||||
| ShortcutGuide | Display keyboard shortcuts. | [ShortcutGuide module][26] |
|
||||
| Workspaces | Save and restore application sets. | [Workspaces module][27] |
|
||||
| ZoomIt | Screen zoom and annotation tool. | [ZoomIt module][28] |
|
||||
|
||||
## Common operations
|
||||
|
||||
### List all supported modules
|
||||
|
||||
```powershell
|
||||
PowerToys.DSC.exe modules --resource 'settings'
|
||||
```
|
||||
|
||||
### Get current configuration
|
||||
|
||||
```powershell
|
||||
# Get configuration for a specific module.
|
||||
PowerToys.DSC.exe get --resource 'settings' --module FancyZones
|
||||
|
||||
# Export configuration (identical to get).
|
||||
PowerToys.DSC.exe export --resource 'settings' --module FancyZones
|
||||
```
|
||||
|
||||
### Apply configuration
|
||||
|
||||
```powershell
|
||||
# Set configuration for a module.
|
||||
$input = '{"settings":{...}}'
|
||||
PowerToys.DSC.exe set --resource 'settings' --module FancyZones --input $input
|
||||
```
|
||||
|
||||
### Validate configuration
|
||||
|
||||
```powershell
|
||||
# Test if current state matches desired state.
|
||||
$input = '{"settings":{...}}'
|
||||
PowerToys.DSC.exe test --resource 'settings' --module FancyZones --input $input
|
||||
```
|
||||
|
||||
### Generate schema
|
||||
|
||||
```powershell
|
||||
# Get JSON schema for a module's settings.
|
||||
PowerToys.DSC.exe schema --resource 'settings' --module FancyZones
|
||||
```
|
||||
|
||||
### Generate DSC manifest
|
||||
|
||||
```powershell
|
||||
# Generate manifest for a specific module.
|
||||
$outputDir = "C:\manifests"
|
||||
PowerToys.DSC.exe manifest --resource 'settings' --module FancyZones `
|
||||
--outputDir $outputDir
|
||||
|
||||
# Generate manifests for all modules.
|
||||
PowerToys.DSC.exe manifest --resource 'settings' --outputDir $outputDir
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
For complete examples, see:
|
||||
|
||||
- [Settings Resource Examples][29]
|
||||
- Individual module documentation in the [modules][30] folder
|
||||
|
||||
## See also
|
||||
|
||||
- [Settings Resource Reference][03]
|
||||
- [PowerToys.DSC.exe Command Reference][01]
|
||||
- [Module Documentation][30]
|
||||
- [Microsoft DSC Documentation][31]
|
||||
- [WinGet Configuration Documentation][32]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ./modules/
|
||||
[03]: ./settings-resource.md
|
||||
[04]: ./modules/App.md
|
||||
[05]: ./modules/AdvancedPaste.md
|
||||
[06]: ./modules/AlwaysOnTop.md
|
||||
[07]: ./modules/Awake.md
|
||||
[08]: ./modules/ColorPicker.md
|
||||
[09]: ./modules/CropAndLock.md
|
||||
[10]: ./modules/EnvironmentVariables.md
|
||||
[11]: ./modules/FancyZones.md
|
||||
[12]: ./modules/FileLocksmith.md
|
||||
[13]: ./modules/FindMyMouse.md
|
||||
[14]: ./modules/Hosts.md
|
||||
[15]: ./modules/ImageResizer.md
|
||||
[16]: ./modules/KeyboardManager.md
|
||||
[17]: ./modules/MeasureTool.md
|
||||
[18]: ./modules/MouseHighlighter.md
|
||||
[19]: ./modules/MouseJump.md
|
||||
[20]: ./modules/MousePointerCrosshairs.md
|
||||
[21]: ./modules/Peek.md
|
||||
[22]: ./modules/PowerAccent.md
|
||||
[23]: ./modules/PowerOCR.md
|
||||
[24]: ./modules/PowerRename.md
|
||||
[25]: ./modules/RegistryPreview.md
|
||||
[26]: ./modules/ShortcutGuide.md
|
||||
[27]: ./modules/Workspaces.md
|
||||
[28]: ./modules/ZoomIt.md
|
||||
[29]: ./settings-resource.md#examples
|
||||
[30]: ./modules/
|
||||
[31]: https://learn.microsoft.com/powershell/dsc/overview
|
||||
[32]: https://learn.microsoft.com/windows/package-manager/configuration/
|
||||
458
doc/dsc/settings-resource.md
Normal file
458
doc/dsc/settings-resource.md
Normal file
@@ -0,0 +1,458 @@
|
||||
---
|
||||
description: Reference for the PowerToys DSC settings resource
|
||||
ms.date: 10/18/2025
|
||||
ms.topic: reference
|
||||
title: Settings Resource
|
||||
---
|
||||
|
||||
# Settings Resource
|
||||
|
||||
## Synopsis
|
||||
|
||||
Manages configuration settings for PowerToys utilities (modules).
|
||||
|
||||
## Description
|
||||
|
||||
The `settings` resource provides Microsoft Desired State Configuration (DSC)
|
||||
support for managing PowerToys configuration. It enables declarative
|
||||
configuration of PowerToys utilities, allowing you to define, test, and
|
||||
enforce desired states for each module.
|
||||
|
||||
Each PowerToys utility (module) has its own configurable properties that can
|
||||
be managed through this resource. The settings resource supports standard DSC
|
||||
operations: get, set, test, export, schema, and manifest generation.
|
||||
|
||||
## Supported modules
|
||||
|
||||
The settings resource supports the following PowerToys modules:
|
||||
|
||||
- **App** - General application settings (enable/disable utilities, run at
|
||||
startup, theme, etc.).
|
||||
- **AdvancedPaste** - Advanced clipboard and paste operations.
|
||||
- **AlwaysOnTop** - Window pinning configuration.
|
||||
- **Awake** - Keep-awake timer settings.
|
||||
- **ColorPicker** - Color picker activation and format settings.
|
||||
- **CropAndLock** - Window cropping settings.
|
||||
- **EnvironmentVariables** - Environment variable editor settings.
|
||||
- **FancyZones** - Window layout and zone configuration.
|
||||
- **FileLocksmith** - File lock detection settings.
|
||||
- **FindMyMouse** - Mouse locator settings.
|
||||
- **Hosts** - Hosts file editor settings.
|
||||
- **ImageResizer** - Image resize configuration.
|
||||
- **KeyboardManager** - Key remapping and shortcut settings.
|
||||
- **MeasureTool** - Screen measurement tool settings.
|
||||
- **MouseHighlighter** - Mouse highlighting configuration.
|
||||
- **MouseJump** - Mouse jump navigation settings.
|
||||
- **MousePointerCrosshairs** - Crosshair display settings.
|
||||
- **Peek** - File preview settings.
|
||||
- **PowerAccent** - Accent character selection settings.
|
||||
- **PowerOCR** - Text extraction settings.
|
||||
- **PowerRename** - Bulk rename configuration.
|
||||
- **RegistryPreview** - Registry file preview settings.
|
||||
- **ShortcutGuide** - Keyboard shortcut overlay settings.
|
||||
- **Workspaces** - Application workspace settings.
|
||||
- **ZoomIt** - Screen zoom and annotation settings.
|
||||
|
||||
For detailed property information for each module, see the individual [module
|
||||
documentation][01].
|
||||
|
||||
## Operations
|
||||
|
||||
### List supported modules
|
||||
|
||||
List all modules that can be configured with the settings resource.
|
||||
|
||||
**Direct execution:**
|
||||
|
||||
```powershell
|
||||
# List all configurable modules.
|
||||
PowerToys.DSC.exe modules --resource 'settings'
|
||||
```
|
||||
|
||||
### Get current state
|
||||
|
||||
Retrieve the current configuration state for a module.
|
||||
|
||||
**Direct execution:**
|
||||
|
||||
```powershell
|
||||
# Get current settings for a module.
|
||||
PowerToys.DSC.exe get --resource 'settings' --module <ModuleName>
|
||||
```
|
||||
|
||||
**DSC configuration:**
|
||||
|
||||
```yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Get Awake settings
|
||||
type: Microsoft.PowerToys/AwakeSettings
|
||||
properties: {}
|
||||
```
|
||||
|
||||
**WinGet configuration:**
|
||||
|
||||
```yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Get FancyZones settings
|
||||
type: Microsoft.PowerToys/FancyZonesSettings
|
||||
properties: {}
|
||||
```
|
||||
|
||||
### Export current state
|
||||
|
||||
Export the current configuration state. The output is identical to the `get`
|
||||
operation.
|
||||
|
||||
**Direct execution:**
|
||||
|
||||
```powershell
|
||||
# Export current settings for a module.
|
||||
PowerToys.DSC.exe export --resource 'settings' --module <ModuleName>
|
||||
```
|
||||
|
||||
### Set desired state
|
||||
|
||||
Apply a configuration to a module, updating only the properties that differ
|
||||
from the desired state.
|
||||
|
||||
**Direct execution:**
|
||||
|
||||
```powershell
|
||||
# Set desired configuration for a module.
|
||||
$input = '{
|
||||
"settings": {
|
||||
"properties": {
|
||||
"keepDisplayOn": true,
|
||||
"mode": 1
|
||||
},
|
||||
"name": "Awake",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
}'
|
||||
PowerToys.DSC.exe set --resource 'settings' --module Awake --input $input
|
||||
```
|
||||
|
||||
**DSC configuration:**
|
||||
|
||||
```yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Configure Awake
|
||||
type: Microsoft.PowerToys/AwakeSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
keepDisplayOn: true
|
||||
mode: 1
|
||||
name: Awake
|
||||
version: 0.0.1
|
||||
```
|
||||
|
||||
**WinGet configuration:**
|
||||
|
||||
```yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install and configure PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
|
||||
- name: Configure FancyZones
|
||||
type: Microsoft.PowerToys/FancyZonesSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
fancyzones_shiftDrag: true
|
||||
fancyzones_mouseSwitch: true
|
||||
fancyzones_displayOrWorkAreaChange_moveWindows: true
|
||||
name: FancyZones
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Test desired state
|
||||
|
||||
Verify whether the current configuration matches the desired state.
|
||||
|
||||
**Direct execution:**
|
||||
|
||||
```powershell
|
||||
# Test if current state matches desired state.
|
||||
$input = '{
|
||||
"settings": {
|
||||
"properties": {
|
||||
"keepDisplayOn": true,
|
||||
"mode": 1
|
||||
},
|
||||
"name": "Awake",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
}'
|
||||
PowerToys.DSC.exe test --resource 'settings' --module Awake --input $input
|
||||
```
|
||||
|
||||
The output includes an `_inDesiredState` property indicating whether the
|
||||
configuration matches (`true`) or differs (`false`).
|
||||
|
||||
**DSC configuration:**
|
||||
|
||||
```yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Test Awake configuration
|
||||
type: Microsoft.PowerToys/AwakeSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
keepDisplayOn: true
|
||||
mode: 1
|
||||
name: Awake
|
||||
version: 0.0.1
|
||||
```
|
||||
|
||||
### Get schema
|
||||
|
||||
Generate the JSON schema for a module's settings, describing all configurable
|
||||
properties and their types.
|
||||
|
||||
**Direct execution:**
|
||||
|
||||
```powershell
|
||||
# Get JSON schema for a module.
|
||||
PowerToys.DSC.exe schema --resource 'settings' --module Awake
|
||||
|
||||
# Format for readability.
|
||||
PowerToys.DSC.exe schema --resource 'settings' --module Awake `
|
||||
| ConvertFrom-Json | ConvertTo-Json -Depth 10
|
||||
```
|
||||
|
||||
### Generate manifest
|
||||
|
||||
Create a DSC resource manifest file for one or all modules.
|
||||
|
||||
**Direct execution:**
|
||||
|
||||
```powershell
|
||||
# Generate manifest for a specific module.
|
||||
$outputDir = "C:\manifests"
|
||||
PowerToys.DSC.exe manifest --resource 'settings' --module Awake `
|
||||
--outputDir $outputDir
|
||||
|
||||
# Generate manifests for all modules.
|
||||
PowerToys.DSC.exe manifest --resource 'settings' --outputDir $outputDir
|
||||
|
||||
# Print manifest to console (omit --outputDir).
|
||||
PowerToys.DSC.exe manifest --resource 'settings' --module Awake
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1 - Enable and configure FancyZones
|
||||
|
||||
This example enables FancyZones and configures window dragging behavior using
|
||||
direct execution.
|
||||
|
||||
```powershell
|
||||
# Get current FancyZones settings.
|
||||
$current = PowerToys.DSC.exe get --resource 'settings' --module FancyZones `
|
||||
| ConvertFrom-Json
|
||||
|
||||
# Modify settings.
|
||||
$desired = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
fancyzones_shiftDrag = $true
|
||||
fancyzones_mouseSwitch = $true
|
||||
fancyzones_displayOrWorkAreaChange_moveWindows = $true
|
||||
}
|
||||
name = "FancyZones"
|
||||
version = "1.0"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
# Apply configuration.
|
||||
PowerToys.DSC.exe set --resource 'settings' --module FancyZones `
|
||||
--input $desired
|
||||
```
|
||||
|
||||
### Example 2 - Configure multiple utilities with DSC
|
||||
|
||||
This example configures multiple PowerToys utilities in a single DSC
|
||||
configuration.
|
||||
|
||||
```yaml
|
||||
# powertoys-multi.dsc.yaml
|
||||
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
|
||||
resources:
|
||||
- name: Enable PowerToys utilities
|
||||
type: Microsoft.PowerToys/AppSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
Enabled:
|
||||
Awake: true
|
||||
FancyZones: true
|
||||
PowerRename: true
|
||||
ColorPicker: true
|
||||
name: App
|
||||
version: 1.0
|
||||
|
||||
- name: Configure Awake
|
||||
type: Microsoft.PowerToys/AwakeSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
keepDisplayOn: true
|
||||
mode: 1
|
||||
name: Awake
|
||||
version: 0.0.1
|
||||
|
||||
- name: Configure ColorPicker
|
||||
type: Microsoft.PowerToys/ColorPickerSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
changecursor: true
|
||||
copiedcolorrepresentation: "HEX"
|
||||
name: ColorPicker
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
### Example 3 - Install and configure with WinGet
|
||||
|
||||
This example installs PowerToys and applies configuration using WinGet.
|
||||
|
||||
```yaml
|
||||
# winget-powertoys-setup.yaml
|
||||
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
|
||||
metadata:
|
||||
winget:
|
||||
processor: dscv3
|
||||
resources:
|
||||
- name: Install PowerToys
|
||||
type: Microsoft.WinGet.DSC/WinGetPackage
|
||||
properties:
|
||||
id: Microsoft.PowerToys
|
||||
source: winget
|
||||
ensure: Present
|
||||
|
||||
- name: Configure general settings
|
||||
type: Microsoft.PowerToys/AppSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
run_elevated: true
|
||||
startup: true
|
||||
theme: "dark"
|
||||
name: App
|
||||
version: 1.0
|
||||
|
||||
- name: Configure FancyZones
|
||||
type: Microsoft.PowerToys/FancyZonesSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
fancyzones_shiftDrag: true
|
||||
fancyzones_zoneSetChange_moveWindows: true
|
||||
name: FancyZones
|
||||
version: 1.0
|
||||
|
||||
- name: Configure ImageResizer
|
||||
type: Microsoft.PowerToys/ImageResizerSettings
|
||||
properties:
|
||||
settings:
|
||||
properties:
|
||||
ImageResizerSizes:
|
||||
- Name: Small
|
||||
Width: 854
|
||||
Height: 480
|
||||
Unit: Pixel
|
||||
Fit: Fit
|
||||
- Name: Medium
|
||||
Width: 1920
|
||||
Height: 1080
|
||||
Unit: Pixel
|
||||
Fit: Fit
|
||||
name: ImageResizer
|
||||
version: 1.0
|
||||
```
|
||||
|
||||
Apply the configuration:
|
||||
|
||||
```powershell
|
||||
winget configure winget-powertoys-setup.yaml
|
||||
```
|
||||
|
||||
### Example 4 - Test configuration drift
|
||||
|
||||
This example tests whether the current configuration matches the desired
|
||||
state.
|
||||
|
||||
```powershell
|
||||
# Define desired state.
|
||||
$desired = @{
|
||||
settings = @{
|
||||
properties = @{
|
||||
keepDisplayOn = $true
|
||||
mode = 1
|
||||
}
|
||||
name = "Awake"
|
||||
version = "0.0.1"
|
||||
}
|
||||
} | ConvertTo-Json -Depth 10 -Compress
|
||||
|
||||
# Test for drift.
|
||||
$result = PowerToys.DSC.exe test --resource 'settings' --module Awake `
|
||||
--input $desired | ConvertFrom-Json
|
||||
|
||||
if ($result._inDesiredState) {
|
||||
Write-Host "Configuration is in desired state"
|
||||
} else {
|
||||
Write-Host "Configuration has drifted from desired state"
|
||||
|
||||
# Apply configuration.
|
||||
PowerToys.DSC.exe set --resource 'settings' --module Awake `
|
||||
--input $desired
|
||||
}
|
||||
```
|
||||
|
||||
### Example 5 - Export all module configurations
|
||||
|
||||
This example exports configuration for all modules.
|
||||
|
||||
```powershell
|
||||
# Get list of all modules.
|
||||
$modules = PowerToys.DSC.exe modules --resource 'settings'
|
||||
|
||||
# Export each module's configuration.
|
||||
$configurations = @{}
|
||||
foreach ($module in $modules) {
|
||||
$config = PowerToys.DSC.exe export --resource 'settings' `
|
||||
--module $module | ConvertFrom-Json
|
||||
$configurations[$module] = $config
|
||||
}
|
||||
|
||||
# Save to file.
|
||||
$configurations | ConvertTo-Json -Depth 10 `
|
||||
| Out-File "powertoys-backup.json"
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [PowerToys DSC Overview][02]
|
||||
- [Module Documentation][01]
|
||||
- [WinGet Configuration][03]
|
||||
|
||||
<!-- Link reference definitions -->
|
||||
[01]: ./modules/
|
||||
[02]: ./overview.md
|
||||
[03]: https://learn.microsoft.com/windows/package-manager/configuration/
|
||||
@@ -9,7 +9,7 @@
|
||||
<Fragment>
|
||||
<!-- Resource directories should be added only if the installer is built on the build farm -->
|
||||
<?ifdef env.IsPipeline?>
|
||||
<?foreach ParentDirectory in INSTALLFOLDER;WinUI3AppsInstallFolder;HistoryPluginFolder;CalculatorPluginFolder;FolderPluginFolder;ProgramPluginFolder;ShellPluginFolder;IndexerPluginFolder;UnitConverterPluginFolder;ValueGeneratorPluginFolder;UriPluginFolder;WindowWalkerPluginFolder;OneNotePluginFolder;RegistryPluginFolder;VSCodeWorkspacesPluginFolder;ServicePluginFolder;SystemPluginFolder;TimeDatePluginFolder;WindowsSettingsPluginFolder;WindowsTerminalPluginFolder;WebSearchPluginFolder;PowerToysPluginFolder?>
|
||||
<?foreach ParentDirectory in INSTALLFOLDER;HistoryPluginFolder;CalculatorPluginFolder;FolderPluginFolder;ProgramPluginFolder;ShellPluginFolder;IndexerPluginFolder;UnitConverterPluginFolder;ValueGeneratorPluginFolder;UriPluginFolder;WindowWalkerPluginFolder;OneNotePluginFolder;RegistryPluginFolder;VSCodeWorkspacesPluginFolder;ServicePluginFolder;SystemPluginFolder;TimeDatePluginFolder;WindowsSettingsPluginFolder;WindowsTerminalPluginFolder;WebSearchPluginFolder;PowerToysPluginFolder?>
|
||||
<DirectoryRef Id="$(var.ParentDirectory)">
|
||||
<!-- Resource file directories -->
|
||||
<?foreach Language in $(var.LocLanguageList)?>
|
||||
@@ -171,12 +171,6 @@
|
||||
</RegistryKey>
|
||||
<File Id="FancyZonesEditor_$(var.IdSafeLanguage)_File" Source="$(var.BinDir)\$(var.Language)\PowerToys.FancyZonesEditor.resources.dll" />
|
||||
</Component>
|
||||
<Component Id="ImageResizer_$(var.IdSafeLanguage)_Component" Directory="Resource$(var.IdSafeLanguage)WinUI3AppsInstallFolder" Guid="$(var.CompGUIDPrefix)02">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="ImageResizer_$(var.IdSafeLanguage)_Component" Value="" KeyPath="yes" />
|
||||
</RegistryKey>
|
||||
<File Id="ImageResizer_$(var.IdSafeLanguage)_File" Source="$(var.BinDir)\WinUI3Apps\$(var.Language)\PowerToys.ImageResizer.resources.dll" />
|
||||
</Component>
|
||||
<Component Id="ColorPicker_$(var.IdSafeLanguage)_Component" Directory="Resource$(var.IdSafeLanguage)INSTALLFOLDER" Guid="$(var.CompGUIDPrefix)03">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
|
||||
<RegistryValue Type="string" Name="ColorPicker_$(var.IdSafeLanguage)_Component" Value="" KeyPath="yes" />
|
||||
@@ -459,7 +453,6 @@
|
||||
<RemoveFolder Id="RemoveFolderResourcesResource$(var.IdSafeLanguage)HistoryPluginFolder" Directory="Resource$(var.IdSafeLanguage)HistoryPluginFolder" On="uninstall" />
|
||||
<RemoveFolder Id="RemoveFolderResourcesResource$(var.IdSafeLanguage)PowerToysPluginFolder" Directory="Resource$(var.IdSafeLanguage)PowerToysPluginFolder" On="uninstall" />
|
||||
<RemoveFolder Id="RemoveFolderResourcesResource$(var.IdSafeLanguage)ValueGeneratorPluginFolder" Directory="Resource$(var.IdSafeLanguage)ValueGeneratorPluginFolder" On="uninstall" />
|
||||
<RemoveFolder Id="RemoveFolderResourcesResource$(var.IdSafeLanguage)WinUI3AppsInstallFolder" Directory="Resource$(var.IdSafeLanguage)WinUI3AppsInstallFolder" On="uninstall"/>
|
||||
<?undef IdSafeLanguage?>
|
||||
<?endforeach?>
|
||||
</Component>
|
||||
|
||||
@@ -131,7 +131,25 @@ if ($platform -ceq "arm64") {
|
||||
}
|
||||
|
||||
#BaseApplications
|
||||
# WORKAROUND: Exclude ImageResizer files that leak into the root output directory.
|
||||
# ImageResizerCLI (Exe, SelfContained) has a ProjectReference to ImageResizerUI (WinExe, SelfContained).
|
||||
# MSBuild copies the referenced WinExe's apphost (.exe, .deps.json, .runtimeconfig.json) to the root
|
||||
# output directory as a side effect. These files are incomplete (missing the managed .dll) and should
|
||||
# not be included in the installer. The complete ImageResizer files are in WinUI3Apps/ and are handled
|
||||
# by WinUI3ApplicationsFiles. TODO: Refactor ImageResizer to use a shared Library project instead.
|
||||
Generate-FileList -fileDepsJson "" -fileListName BaseApplicationsFiles -wxsFilePath $PSScriptRoot\BaseApplications.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release"
|
||||
|
||||
# Remove leaked ImageResizer artifacts from BaseApplications
|
||||
$baseAppWxsPath = "$PSScriptRoot\BaseApplications.wxs"
|
||||
$baseAppWxs = Get-Content $baseAppWxsPath -Raw
|
||||
$baseAppWxs = $baseAppWxs -replace 'PowerToys\.ImageResizer\.exe;?', ''
|
||||
$baseAppWxs = $baseAppWxs -replace 'PowerToys\.ImageResizer\.deps\.json;?', ''
|
||||
$baseAppWxs = $baseAppWxs -replace 'PowerToys\.ImageResizer\.runtimeconfig\.json;?', ''
|
||||
# Clean up trailing/double semicolons left after removal
|
||||
$baseAppWxs = $baseAppWxs -replace ';;+', ';'
|
||||
$baseAppWxs = $baseAppWxs -replace '=;', '='
|
||||
$baseAppWxs = $baseAppWxs -replace ';"', '"'
|
||||
Set-Content -Path $baseAppWxsPath -Value $baseAppWxs
|
||||
Generate-FileComponents -fileListName "BaseApplicationsFiles" -wxsFilePath $PSScriptRoot\BaseApplications.wxs
|
||||
|
||||
#WinUI3Applications
|
||||
|
||||
@@ -105,6 +105,22 @@ namespace Microsoft.PowerToys.Common.UI.Controls
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case nameof(VirtualKey.Up):
|
||||
SetGlyphOrText("\uE0E4", VirtualKey.Up);
|
||||
break;
|
||||
|
||||
case nameof(VirtualKey.Down):
|
||||
SetGlyphOrText("\uE0E5", VirtualKey.Down);
|
||||
break;
|
||||
|
||||
case nameof(VirtualKey.Left):
|
||||
SetGlyphOrText("\uE0E2", VirtualKey.Left);
|
||||
break;
|
||||
|
||||
case nameof(VirtualKey.Right):
|
||||
SetGlyphOrText("\uE0E3", VirtualKey.Right);
|
||||
break;
|
||||
|
||||
case "Copilot":
|
||||
_keyPresenter.Style = (Style)Application.Current.Resources["CopilotKeyCharPresenterStyle"];
|
||||
break;
|
||||
@@ -141,19 +157,19 @@ namespace Microsoft.PowerToys.Common.UI.Controls
|
||||
break;
|
||||
|
||||
case VirtualKey.Up:
|
||||
_keyPresenter.Content = "\uE0E4";
|
||||
SetGlyphOrText("\uE0E4", virtualKey);
|
||||
break;
|
||||
|
||||
case VirtualKey.Down:
|
||||
_keyPresenter.Content = "\uE0E5";
|
||||
SetGlyphOrText("\uE0E5", virtualKey);
|
||||
break;
|
||||
|
||||
case VirtualKey.Left:
|
||||
_keyPresenter.Content = "\uE0E2";
|
||||
SetGlyphOrText("\uE0E2", virtualKey);
|
||||
break;
|
||||
|
||||
case VirtualKey.Right:
|
||||
_keyPresenter.Content = "\uE0E3";
|
||||
SetGlyphOrText("\uE0E3", virtualKey);
|
||||
break;
|
||||
|
||||
case VirtualKey.LeftWindows:
|
||||
|
||||
@@ -20,9 +20,4 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ui\ImageResizerUI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Force using WindowsDesktop runtime to ensure consistent dll versions with other projects -->
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.WindowsDesktop.App.WPF" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -50,10 +50,10 @@
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<ModuleDefinitionFile>Source.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
<PreBuildEvent>
|
||||
<Command>del $(OutDir)\ImageResizerContextMenuPackage.msix /q
|
||||
MakeAppx.exe pack /d . /p $(OutDir)ImageResizerContextMenuPackage.msix /nv</Command>
|
||||
</PreBuildEvent>
|
||||
<PostBuildEvent>
|
||||
<Command>if exist "$(OutDir)ImageResizerContextMenuPackage.msix" del "$(OutDir)ImageResizerContextMenuPackage.msix" /q
|
||||
MakeAppx.exe pack /d "$(MSBuildThisFileDirectory)." /p "$(OutDir)ImageResizerContextMenuPackage.msix" /nv</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
||||
<ClCompile>
|
||||
@@ -73,10 +73,10 @@ MakeAppx.exe pack /d . /p $(OutDir)ImageResizerContextMenuPackage.msix /nv</Comm
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<ModuleDefinitionFile>Source.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
<PreBuildEvent>
|
||||
<Command>del $(OutDir)\ImageResizerContextMenuPackage.msix /q
|
||||
MakeAppx.exe pack /d . /p $(OutDir)ImageResizerContextMenuPackage.msix /nv</Command>
|
||||
</PreBuildEvent>
|
||||
<PostBuildEvent>
|
||||
<Command>if exist "$(OutDir)ImageResizerContextMenuPackage.msix" del "$(OutDir)ImageResizerContextMenuPackage.msix" /q
|
||||
MakeAppx.exe pack /d "$(MSBuildThisFileDirectory)." /p "$(OutDir)ImageResizerContextMenuPackage.msix" /nv</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="framework.h" />
|
||||
|
||||
@@ -25,24 +25,3 @@ using System.Diagnostics.CodeAnalysis;
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.SpecialRules", "SA0001:XmlCommentAnalysisDisabled", Justification = "Not enabled as we don't want or need XML documentation.")]
|
||||
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1629:DocumentationTextMustEndWithAPeriod", Justification = "Not enabled as we don't want or need XML documentation.")]
|
||||
|
||||
[assembly: SuppressMessage("Microsoft.Design", "CA1009:DeclareEventHandlersCorrectly", Scope = "member", Target = "Microsoft.Templates.Core.Locations.TemplatesSynchronization.#SyncStatusChanged", Justification = "Using an Action<object, SyncStatusEventArgs> does not allow the required notation")]
|
||||
|
||||
// Non general suppressions
|
||||
[assembly: SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "This is part of the markdown processing", MessageId = "System.Windows.Documents.Run.#ctor(System.String)", Scope = "member", Target = "Microsoft.Templates.UI.Controls.Markdown.#ImageInlineEvaluator(System.Text.RegularExpressions.Match)")]
|
||||
[assembly: SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "We need to have the names of these keys in lowercase to be able to compare with the keys becoming form the template json. ContainsKey does not allow StringComparer specification to IgnoreCase", Scope = "member", Target = "Microsoft.Templates.Core.ITemplateInfoExtensions.#GetQueryableProperties(Microsoft.TemplateEngine.Abstractions.ITemplateInfo)")]
|
||||
[assembly: SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "We need to have the names of these keys in lowercase to be able to compare with the keys becoming form the template json. ContainsKey does not allow StringComparer specification to IgnoreCase", Scope = "member", Target = "Microsoft.Templates.Core.Composition.CompositionQuery.#Match(System.Collections.Generic.IEnumerable`1<Microsoft.Templates.Core.Composition.QueryNode>,Microsoft.Templates.Core.Composition.QueryablePropertyDictionary)")]
|
||||
[assembly: SuppressMessage("Usage", "VSTHRD103:Call async methods when in an async method", Justification = "Resource DictionaryWriter does not implement flush async", Scope = "member", Target = "~M:Microsoft.Templates.Core.PostActions.Catalog.Merge.MergeResourceDictionaryPostAction.ExecuteInternalAsync~System.Threading.Tasks.Task")]
|
||||
|
||||
// Threading suppressions
|
||||
[assembly: SuppressMessage("Microsoft.VisualStudio.Threading.Analyzers", "VSTHRD100:Avoid async void methods", Justification = "Event handlers needs async void", Scope = "member", Target = "~M:Microsoft.Templates.UI.Controls.Notification.OnClose")]
|
||||
[assembly: SuppressMessage("Microsoft.VisualStudio.Threading.Analyzers", "VSTHRD100:Avoid async void methods", Justification = "Event handlers needs async void", Scope = "member", Target = "~M:Microsoft.Templates.UI.ViewModels.Common.SavedTemplateViewModel.OnDelete")]
|
||||
[assembly: SuppressMessage("Microsoft.VisualStudio.Threading.Analyzers", "VSTHRD100:Avoid async void methods", Justification = "Event handlers needs async void", Scope = "member", Target = "~M:Microsoft.Templates.UI.ViewModels.Common.WizardNavigation.GoBack")]
|
||||
[assembly: SuppressMessage("Microsoft.VisualStudio.Threading.Analyzers", "VSTHRD100:Avoid async void methods", Justification = "Event handlers needs async void", Scope = "member", Target = "~M:Microsoft.Templates.UI.ViewModels.Common.WizardNavigation.GoForward")]
|
||||
[assembly: SuppressMessage("Microsoft.VisualStudio.Threading.Analyzers", "VSTHRD100:Avoid async void methods", Justification = "Event handlers needs async void", Scope = "member", Target = "~M:Microsoft.Templates.UI.ViewModels.Common.SavedTemplateViewModel.OnDelete(Microsoft.Templates.UI.ViewModels.Common.SavedTemplateViewModel)")]
|
||||
|
||||
// Localization suppressions
|
||||
[assembly: SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Microsoft.Templates.Core.Locations.JunctionNativeMethods.ThrowLastWin32Error(System.String)", Scope = "member", Target = "Microsoft.Templates.Core.Locations.JunctionNativeMethods.#CreateJunction(System.String,System.String,System.Boolean)", Justification = "Only used for local generation")]
|
||||
[assembly: SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Microsoft.Templates.Core.Locations.JunctionNativeMethods.ThrowLastWin32Error(System.String)", Scope = "member", Target = "Microsoft.Templates.Core.Locations.JunctionNativeMethods.#DeleteJunction(System.String)", Justification = "Only used for local generation")]
|
||||
[assembly: SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Microsoft.Templates.Core.Locations.JunctionNativeMethods.ThrowLastWin32Error(System.String)", Scope = "member", Target = "Microsoft.Templates.Core.Locations.JunctionNativeMethods.#InternalGetTarget(Microsoft.Win32.SafeHandles.SafeFileHandle)", Justification = "Only used for local generation")]
|
||||
[assembly: SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Microsoft.Templates.Core.Locations.JunctionNativeMethods.ThrowLastWin32Error(System.String)", Scope = "member", Target = "Microsoft.Templates.Core.Locations.JunctionNativeMethods.#OpenReparsePoint(System.String,Microsoft.Templates.Core.Locations.JunctionNativeMethods+EFileAccess)", Justification = "Only used for local generation")]
|
||||
[assembly: SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Windows.Documents.InlineCollection.Add(System.String)", Scope = "member", Target = "Microsoft.Templates.UI.Extensions.TextBlockExtensions.#OnSequentialFlowStepChanged(System.Windows.DependencyObject,System.Windows.DependencyPropertyChangedEventArgs)", Justification = "No text here")]
|
||||
|
||||
@@ -82,4 +82,4 @@ IDR_CONTEXTMENUHANDLER REGISTRY "ContextMenuHandler.rgs"
|
||||
|
||||
// Icon with lowest ID value placed first to ensure application icon
|
||||
// remains consistent on all systems.
|
||||
IDI_RESIZE_PICTURES ICON "..\ui\Resources\ImageResizer.ico"
|
||||
IDI_RESIZE_PICTURES ICON "..\\ui\\Assets\\ImageResizer\\ImageResizer.ico"
|
||||
|
||||
@@ -1,62 +1,64 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<!-- Look at Directory.Build.props in root for common stuff as well -->
|
||||
<Import Project="$(RepoRoot)src\Common.Dotnet.CsWinRT.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}</ProjectGuid>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<!-- Look at Directory.Build.props in root for common stuff as well -->
|
||||
<Import Project="$(RepoRoot)src\Common.Dotnet.CsWinRT.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<SelfContained>true</SelfContained>
|
||||
<RuntimeIdentifier Condition="'$(Platform)' == 'x64'">win-x64</RuntimeIdentifier>
|
||||
<RuntimeIdentifier Condition="'$(Platform)' == 'ARM64'">win-arm64</RuntimeIdentifier>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>ImageResizer</RootNamespace>
|
||||
<AssemblyName>ImageResizer.Test</AssemblyName>
|
||||
|
||||
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\tests\$(AssemblyName)\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="TestMetadataIssue1928.jpg" />
|
||||
<None Remove="TestMetadataIssue1928_NoMetadata.jpg" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ui\ImageResizerUI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Test.gif">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Test.ico">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Test.jpg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Test.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="TestMetadataIssue1928.jpg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="TestMetadataIssue1928_NoMetadata.jpg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="TestMetadataIssue2447.jpg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="TestPortrait.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Moq" />
|
||||
<PackageReference Include="MSTest" />
|
||||
<PackageReference Include="System.IO.Abstractions" />
|
||||
<PackageReference Include="System.CodeDom">
|
||||
<!-- This package is a dependency of System.Management, but we need to set it here so we can exclude the assets, so it doesn't conflict with the 8.0.1 dll coming from .NET SDK. -->
|
||||
<ExcludeAssets>runtime</ExcludeAssets> <!-- Should already be present on .net sdk runtime, so we avoid the conflicting runtime version from nuget -->
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
<RootNamespace>ImageResizer</RootNamespace>
|
||||
<AssemblyName>ImageResizer.Test</AssemblyName>
|
||||
|
||||
<OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\tests\$(AssemblyName)\</OutputPath>
|
||||
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="TestMetadataIssue1928.jpg" />
|
||||
<None Remove="TestMetadataIssue1928_NoMetadata.jpg" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ui\ImageResizerUI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Test.gif">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Test.ico">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Test.jpg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="Test.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="TestMetadataIssue1928.jpg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="TestMetadataIssue1928_NoMetadata.jpg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="TestMetadataIssue2447.jpg">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="TestPortrait.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Moq" />
|
||||
<PackageReference Include="MSTest" />
|
||||
<PackageReference Include="System.IO.Abstractions" />
|
||||
<PackageReference Include="System.CodeDom">
|
||||
<!-- This package is a dependency of System.Management, but we need to set it here so we can exclude the assets, so it doesn't conflict with the 8.0.1 dll coming from .NET SDK. -->
|
||||
<ExcludeAssets>runtime</ExcludeAssets> <!-- Should already be present on .net sdk runtime, so we avoid the conflicting runtime version from nuget -->
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using ImageResizer.Properties;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace ImageResizer.Models
|
||||
{
|
||||
[TestClass]
|
||||
public class CustomSizeTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void NameWorks()
|
||||
{
|
||||
var size = new CustomSize
|
||||
{
|
||||
Name = "Ignored",
|
||||
};
|
||||
|
||||
Assert.AreEqual(Resources.Input_Custom, size.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ImageResizer.Properties;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
@@ -50,29 +51,14 @@ namespace ImageResizer.Models
|
||||
Assert.AreEqual("OutputDir", result.DestinationDirectory);
|
||||
}
|
||||
|
||||
/*[Fact]
|
||||
public void Process_executes_in_parallel()
|
||||
{
|
||||
var batch = CreateBatch(_ => Thread.Sleep(50));
|
||||
batch.Files.AddRange(
|
||||
Enumerable.Range(0, Environment.ProcessorCount)
|
||||
.Select(i => "Image" + i + ".jpg"));
|
||||
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
batch.Process(CancellationToken.None, (_, __) => { });
|
||||
stopwatch.Stop();
|
||||
|
||||
Assert.InRange(stopwatch.ElapsedMilliseconds, 50, 99);
|
||||
}*/
|
||||
|
||||
[TestMethod]
|
||||
public void ProcessAggregatesErrors()
|
||||
public async Task ProcessAggregatesErrors()
|
||||
{
|
||||
var batch = CreateBatch(file => throw new InvalidOperationException("Error: " + file));
|
||||
batch.Files.Add("Image1.jpg");
|
||||
batch.Files.Add("Image2.jpg");
|
||||
|
||||
var errors = batch.Process((_, __) => { }, CancellationToken.None).ToList();
|
||||
var errors = (await batch.ProcessAsync((_, __) => { }, CancellationToken.None)).ToList();
|
||||
|
||||
Assert.AreEqual(2, errors.Count);
|
||||
|
||||
@@ -91,14 +77,14 @@ namespace ImageResizer.Models
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ProcessReportsProgress()
|
||||
public async Task ProcessReportsProgress()
|
||||
{
|
||||
var batch = CreateBatch(_ => { });
|
||||
batch.Files.Add("Image1.jpg");
|
||||
batch.Files.Add("Image2.jpg");
|
||||
var calls = new ConcurrentBag<(int I, double Count)>();
|
||||
|
||||
batch.Process(
|
||||
await batch.ProcessAsync(
|
||||
(i, count) => calls.Add((i, count)),
|
||||
CancellationToken.None);
|
||||
|
||||
@@ -109,8 +95,12 @@ namespace ImageResizer.Models
|
||||
{
|
||||
var mock = new Mock<ResizeBatch> { CallBase = true };
|
||||
mock.Protected()
|
||||
.Setup("Execute", ItExpr.IsAny<string>(), ItExpr.IsAny<Settings>())
|
||||
.Callback((string file, Settings settings) => executeAction(file));
|
||||
.Setup<Task>("ExecuteAsync", ItExpr.IsAny<string>(), ItExpr.IsAny<Settings>())
|
||||
.Returns((string file, Settings settings) =>
|
||||
{
|
||||
executeAction(file);
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
return mock.Object;
|
||||
}
|
||||
|
||||
@@ -7,10 +7,8 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using ImageResizer.Extensions;
|
||||
using ImageResizer.Properties;
|
||||
using ImageResizer.Test;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
@@ -20,45 +18,61 @@ namespace ImageResizer.Models
|
||||
[TestClass]
|
||||
public class ResizeOperationTests : IDisposable
|
||||
{
|
||||
// Known legacy container format GUID for PNG, used as FallbackEncoder value in settings JSON
|
||||
private static readonly Guid PngContainerFormatGuid = new Guid("1b7cfaf4-713f-473c-bbcd-6137425faeaf");
|
||||
|
||||
private static readonly string[] CommentPropertyQuery = new[] { "System.Comment" };
|
||||
private static readonly string[] DateTakenPropertyQuery = new[] { "System.Photo.DateTaken" };
|
||||
private static readonly string[] CameraModelPropertyQuery = new[] { "System.Photo.CameraModel" };
|
||||
|
||||
private readonly TestDirectory _directory = new TestDirectory();
|
||||
private bool disposedValue;
|
||||
|
||||
[TestMethod]
|
||||
public void ExecuteCopiesFrameMetadata()
|
||||
public async Task ExecuteCopiesFrameMetadata()
|
||||
{
|
||||
var operation = new ResizeOperation("Test.jpg", _directory, Settings());
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
AssertEx.Image(
|
||||
await AssertEx.ImageAsync(
|
||||
_directory.File(),
|
||||
image => Assert.AreEqual("Test", ((BitmapMetadata)image.Frames[0].Metadata).Comment));
|
||||
async decoder =>
|
||||
{
|
||||
var props = await decoder.BitmapProperties.GetPropertiesAsync(CommentPropertyQuery);
|
||||
Assert.IsTrue(props.ContainsKey("System.Comment"), "Comment metadata should be preserved during transcode");
|
||||
Assert.AreEqual("Test", (string)props["System.Comment"].Value, "Comment value should be preserved");
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ExecuteCopiesFrameMetadataEvenWhenMetadataCannotBeCloned()
|
||||
public async Task ExecuteCopiesFrameMetadataEvenWhenMetadataCannotBeCloned()
|
||||
{
|
||||
var operation = new ResizeOperation("TestMetadataIssue2447.jpg", _directory, Settings());
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
AssertEx.Image(
|
||||
await AssertEx.ImageAsync(
|
||||
_directory.File(),
|
||||
image => Assert.IsNotNull(((BitmapMetadata)image.Frames[0].Metadata).CameraModel));
|
||||
async decoder =>
|
||||
{
|
||||
var props = await decoder.BitmapProperties.GetPropertiesAsync(CameraModelPropertyQuery);
|
||||
Assert.IsTrue(props.ContainsKey("System.Photo.CameraModel"), "Camera model metadata should be preserved");
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ExecuteKeepsDateModified()
|
||||
public async Task ExecuteKeepsDateModified()
|
||||
{
|
||||
var operation = new ResizeOperation("Test.png", _directory, Settings(s => s.KeepDateModified = true));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
Assert.AreEqual(File.GetLastWriteTimeUtc("Test.png"), File.GetLastWriteTimeUtc(_directory.File()));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ExecuteKeepsDateModifiedWhenReplacingOriginals()
|
||||
public async Task ExecuteKeepsDateModifiedWhenReplacingOriginals()
|
||||
{
|
||||
var path = Path.Combine(_directory, "Test.png");
|
||||
File.Copy("Test.png", path);
|
||||
@@ -75,55 +89,59 @@ namespace ImageResizer.Models
|
||||
s.Replace = true;
|
||||
}));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
Assert.AreEqual(originalDateModified, File.GetLastWriteTimeUtc(_directory.File()));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ExecuteReplacesOriginals()
|
||||
public async Task ExecuteReplacesOriginals()
|
||||
{
|
||||
var path = Path.Combine(_directory, "Test.png");
|
||||
File.Copy("Test.png", path);
|
||||
|
||||
var operation = new ResizeOperation(path, null, Settings(s => s.Replace = true));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
AssertEx.Image(_directory.File(), image => Assert.AreEqual(96, image.Frames[0].PixelWidth));
|
||||
await AssertEx.ImageAsync(_directory.File(), decoder => Assert.AreEqual(96u, decoder.PixelWidth));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ExecuteTransformsEachFrame()
|
||||
public async Task ExecuteTransformsEachFrame()
|
||||
{
|
||||
var operation = new ResizeOperation("Test.gif", _directory, Settings());
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
AssertEx.Image(
|
||||
await AssertEx.ImageAsync(
|
||||
_directory.File(),
|
||||
image =>
|
||||
async decoder =>
|
||||
{
|
||||
Assert.AreEqual(2, image.Frames.Count);
|
||||
AssertEx.All(image.Frames, frame => Assert.AreEqual(96, frame.PixelWidth));
|
||||
Assert.AreEqual(2u, decoder.FrameCount);
|
||||
for (uint i = 0; i < decoder.FrameCount; i++)
|
||||
{
|
||||
var frame = await decoder.GetFrameAsync(i);
|
||||
Assert.AreEqual(96u, frame.PixelWidth);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ExecuteUsesFallbackEncoder()
|
||||
public async Task ExecuteUsesFallbackEncoder()
|
||||
{
|
||||
var operation = new ResizeOperation(
|
||||
"Test.ico",
|
||||
_directory,
|
||||
Settings(s => s.FallbackEncoder = new PngBitmapEncoder().CodecInfo.ContainerFormat));
|
||||
Settings(s => s.FallbackEncoder = PngContainerFormatGuid));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
CollectionAssert.Contains(_directory.FileNames.ToList(), "Test (Test).png");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TransformIgnoresOrientationWhenLandscapeToPortrait()
|
||||
public async Task TransformIgnoresOrientationWhenLandscapeToPortrait()
|
||||
{
|
||||
var operation = new ResizeOperation(
|
||||
"Test.png",
|
||||
@@ -136,19 +154,19 @@ namespace ImageResizer.Models
|
||||
x.SelectedSize.Height = 192;
|
||||
}));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
AssertEx.Image(
|
||||
await AssertEx.ImageAsync(
|
||||
_directory.File(),
|
||||
image =>
|
||||
decoder =>
|
||||
{
|
||||
Assert.AreEqual(192, image.Frames[0].PixelWidth);
|
||||
Assert.AreEqual(96, image.Frames[0].PixelHeight);
|
||||
Assert.AreEqual(192u, decoder.PixelWidth);
|
||||
Assert.AreEqual(96u, decoder.PixelHeight);
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TransformIgnoresOrientationWhenPortraitToLandscape()
|
||||
public async Task TransformIgnoresOrientationWhenPortraitToLandscape()
|
||||
{
|
||||
var operation = new ResizeOperation(
|
||||
"TestPortrait.png",
|
||||
@@ -161,19 +179,19 @@ namespace ImageResizer.Models
|
||||
x.SelectedSize.Height = 96;
|
||||
}));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
AssertEx.Image(
|
||||
await AssertEx.ImageAsync(
|
||||
_directory.File(),
|
||||
image =>
|
||||
decoder =>
|
||||
{
|
||||
Assert.AreEqual(96, image.Frames[0].PixelWidth);
|
||||
Assert.AreEqual(192, image.Frames[0].PixelHeight);
|
||||
Assert.AreEqual(96u, decoder.PixelWidth);
|
||||
Assert.AreEqual(192u, decoder.PixelHeight);
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TransformIgnoresIgnoreOrientationWhenAuto()
|
||||
public async Task TransformIgnoresIgnoreOrientationWhenAuto()
|
||||
{
|
||||
var operation = new ResizeOperation(
|
||||
"Test.png",
|
||||
@@ -186,19 +204,19 @@ namespace ImageResizer.Models
|
||||
x.SelectedSize.Height = 0;
|
||||
}));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
AssertEx.Image(
|
||||
await AssertEx.ImageAsync(
|
||||
_directory.File(),
|
||||
image =>
|
||||
decoder =>
|
||||
{
|
||||
Assert.AreEqual(96, image.Frames[0].PixelWidth);
|
||||
Assert.AreEqual(48, image.Frames[0].PixelHeight);
|
||||
Assert.AreEqual(96u, decoder.PixelWidth);
|
||||
Assert.AreEqual(48u, decoder.PixelHeight);
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TransformIgnoresIgnoreOrientationWhenPercent()
|
||||
public async Task TransformIgnoresIgnoreOrientationWhenPercent()
|
||||
{
|
||||
var operation = new ResizeOperation(
|
||||
"Test.png",
|
||||
@@ -213,19 +231,19 @@ namespace ImageResizer.Models
|
||||
x.SelectedSize.Fit = ResizeFit.Stretch;
|
||||
}));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
AssertEx.Image(
|
||||
await AssertEx.ImageAsync(
|
||||
_directory.File(),
|
||||
image =>
|
||||
decoder =>
|
||||
{
|
||||
Assert.AreEqual(96, image.Frames[0].PixelWidth);
|
||||
Assert.AreEqual(192, image.Frames[0].PixelHeight);
|
||||
Assert.AreEqual(96u, decoder.PixelWidth);
|
||||
Assert.AreEqual(192u, decoder.PixelHeight);
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TransformHonorsShrinkOnly()
|
||||
public async Task TransformHonorsShrinkOnly()
|
||||
{
|
||||
var operation = new ResizeOperation(
|
||||
"Test.png",
|
||||
@@ -238,19 +256,19 @@ namespace ImageResizer.Models
|
||||
x.SelectedSize.Height = 288;
|
||||
}));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
AssertEx.Image(
|
||||
await AssertEx.ImageAsync(
|
||||
_directory.File(),
|
||||
image =>
|
||||
decoder =>
|
||||
{
|
||||
Assert.AreEqual(192, image.Frames[0].PixelWidth);
|
||||
Assert.AreEqual(96, image.Frames[0].PixelHeight);
|
||||
Assert.AreEqual(192u, decoder.PixelWidth);
|
||||
Assert.AreEqual(96u, decoder.PixelHeight);
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TransformIgnoresShrinkOnlyWhenPercent()
|
||||
public async Task TransformIgnoresShrinkOnlyWhenPercent()
|
||||
{
|
||||
var operation = new ResizeOperation(
|
||||
"Test.png",
|
||||
@@ -263,19 +281,19 @@ namespace ImageResizer.Models
|
||||
x.SelectedSize.Unit = ResizeUnit.Percent;
|
||||
}));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
AssertEx.Image(
|
||||
await AssertEx.ImageAsync(
|
||||
_directory.File(),
|
||||
image =>
|
||||
decoder =>
|
||||
{
|
||||
Assert.AreEqual(256, image.Frames[0].PixelWidth);
|
||||
Assert.AreEqual(128, image.Frames[0].PixelHeight);
|
||||
Assert.AreEqual(256u, decoder.PixelWidth);
|
||||
Assert.AreEqual(128u, decoder.PixelHeight);
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TransformHonorsShrinkOnlyWhenAutoHeight()
|
||||
public async Task TransformHonorsShrinkOnlyWhenAutoHeight()
|
||||
{
|
||||
var operation = new ResizeOperation(
|
||||
"Test.png",
|
||||
@@ -288,15 +306,15 @@ namespace ImageResizer.Models
|
||||
x.SelectedSize.Height = 0;
|
||||
}));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
AssertEx.Image(
|
||||
await AssertEx.ImageAsync(
|
||||
_directory.File(),
|
||||
image => Assert.AreEqual(192, image.Frames[0].PixelWidth));
|
||||
decoder => Assert.AreEqual(192u, decoder.PixelWidth));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TransformHonorsShrinkOnlyWhenAutoWidth()
|
||||
public async Task TransformHonorsShrinkOnlyWhenAutoWidth()
|
||||
{
|
||||
var operation = new ResizeOperation(
|
||||
"Test.png",
|
||||
@@ -309,15 +327,15 @@ namespace ImageResizer.Models
|
||||
x.SelectedSize.Height = 288;
|
||||
}));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
AssertEx.Image(
|
||||
await AssertEx.ImageAsync(
|
||||
_directory.File(),
|
||||
image => Assert.AreEqual(96, image.Frames[0].PixelHeight));
|
||||
decoder => Assert.AreEqual(96u, decoder.PixelHeight));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TransformHonorsUnit()
|
||||
public async Task TransformHonorsUnit()
|
||||
{
|
||||
var operation = new ResizeOperation(
|
||||
"Test.png",
|
||||
@@ -330,82 +348,79 @@ namespace ImageResizer.Models
|
||||
x.SelectedSize.Unit = ResizeUnit.Inch;
|
||||
}));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
AssertEx.Image(_directory.File(), image => Assert.AreEqual(Math.Ceiling(image.Frames[0].DpiX), image.Frames[0].PixelWidth));
|
||||
await AssertEx.ImageAsync(_directory.File(), decoder => Assert.AreEqual((uint)Math.Ceiling(decoder.DpiX), decoder.PixelWidth));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TransformHonorsFitWhenFit()
|
||||
public async Task TransformHonorsFitWhenFit()
|
||||
{
|
||||
var operation = new ResizeOperation(
|
||||
"Test.png",
|
||||
_directory,
|
||||
Settings(x => x.SelectedSize.Fit = ResizeFit.Fit));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
AssertEx.Image(
|
||||
await AssertEx.ImageAsync(
|
||||
_directory.File(),
|
||||
image =>
|
||||
decoder =>
|
||||
{
|
||||
Assert.AreEqual(96, image.Frames[0].PixelWidth);
|
||||
Assert.AreEqual(48, image.Frames[0].PixelHeight);
|
||||
Assert.AreEqual(96u, decoder.PixelWidth);
|
||||
Assert.AreEqual(48u, decoder.PixelHeight);
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TransformHonorsFitWhenFill()
|
||||
public async Task TransformHonorsFitWhenFill()
|
||||
{
|
||||
var operation = new ResizeOperation(
|
||||
"Test.png",
|
||||
_directory,
|
||||
Settings(x => x.SelectedSize.Fit = ResizeFit.Fill));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
AssertEx.Image(
|
||||
await AssertEx.ImageAsync(
|
||||
_directory.File(),
|
||||
image =>
|
||||
async decoder =>
|
||||
{
|
||||
Assert.AreEqual(Colors.White, image.Frames[0].GetFirstPixel());
|
||||
Assert.AreEqual(96, image.Frames[0].PixelWidth);
|
||||
Assert.AreEqual(96, image.Frames[0].PixelHeight);
|
||||
var pixel = await decoder.GetFirstPixelAsync();
|
||||
Assert.AreEqual((byte)255, pixel.R, "First pixel R should be 255 (white)");
|
||||
Assert.AreEqual((byte)255, pixel.G, "First pixel G should be 255 (white)");
|
||||
Assert.AreEqual((byte)255, pixel.B, "First pixel B should be 255 (white)");
|
||||
Assert.AreEqual(96u, decoder.PixelWidth);
|
||||
Assert.AreEqual(96u, decoder.PixelHeight);
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TransformHonorsFitWhenStretch()
|
||||
public async Task TransformHonorsFitWhenStretch()
|
||||
{
|
||||
var operation = new ResizeOperation(
|
||||
"Test.png",
|
||||
_directory,
|
||||
Settings(x => x.SelectedSize.Fit = ResizeFit.Stretch));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
AssertEx.Image(
|
||||
await AssertEx.ImageAsync(
|
||||
_directory.File(),
|
||||
image =>
|
||||
async decoder =>
|
||||
{
|
||||
Assert.AreEqual(Colors.Black, image.Frames[0].GetFirstPixel());
|
||||
Assert.AreEqual(96, image.Frames[0].PixelWidth);
|
||||
Assert.AreEqual(96, image.Frames[0].PixelHeight);
|
||||
var pixel = await decoder.GetFirstPixelAsync();
|
||||
Assert.AreEqual((byte)0, pixel.R, "First pixel R should be 0 (black)");
|
||||
Assert.AreEqual((byte)0, pixel.G, "First pixel G should be 0 (black)");
|
||||
Assert.AreEqual((byte)0, pixel.B, "First pixel B should be 0 (black)");
|
||||
Assert.AreEqual(96u, decoder.PixelWidth);
|
||||
Assert.AreEqual(96u, decoder.PixelHeight);
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TransformHonorsFillWithShrinkOnlyWhenCropRequired()
|
||||
public async Task TransformHonorsFillWithShrinkOnlyWhenCropRequired()
|
||||
{
|
||||
// Testing original 96x96 pixel Test.jpg cropped to 48x96 (Fill mode).
|
||||
//
|
||||
// ScaleX = 48/96 = 0.5
|
||||
// ScaleY = 96/96 = 1.0
|
||||
// Fill mode takes the max of these = 1.0.
|
||||
//
|
||||
// Previously, the transform logic saw the scale of 1.0 and returned the
|
||||
// original dimensions. The corrected logic recognizes that a crop is
|
||||
// required on one dimension and proceeds with the operation.
|
||||
var operation = new ResizeOperation(
|
||||
"Test.jpg",
|
||||
_directory,
|
||||
@@ -417,22 +432,20 @@ namespace ImageResizer.Models
|
||||
x.SelectedSize.Height = 96;
|
||||
}));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
AssertEx.Image(
|
||||
await AssertEx.ImageAsync(
|
||||
_directory.File(),
|
||||
image =>
|
||||
decoder =>
|
||||
{
|
||||
Assert.AreEqual(48, image.Frames[0].PixelWidth);
|
||||
Assert.AreEqual(96, image.Frames[0].PixelHeight);
|
||||
Assert.AreEqual(48u, decoder.PixelWidth);
|
||||
Assert.AreEqual(96u, decoder.PixelHeight);
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TransformHonorsFillWithShrinkOnlyWhenUpscaleAttempted()
|
||||
public async Task TransformHonorsFillWithShrinkOnlyWhenUpscaleAttempted()
|
||||
{
|
||||
// Confirm that attempting to upscale the original image will return the
|
||||
// original dimensions when Shrink Only is enabled.
|
||||
var operation = new ResizeOperation(
|
||||
"Test.jpg",
|
||||
_directory,
|
||||
@@ -444,21 +457,20 @@ namespace ImageResizer.Models
|
||||
x.SelectedSize.Height = 192;
|
||||
}));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
AssertEx.Image(
|
||||
await AssertEx.ImageAsync(
|
||||
_directory.File(),
|
||||
image =>
|
||||
decoder =>
|
||||
{
|
||||
Assert.AreEqual(96, image.Frames[0].PixelWidth);
|
||||
Assert.AreEqual(96, image.Frames[0].PixelHeight);
|
||||
Assert.AreEqual(96u, decoder.PixelWidth);
|
||||
Assert.AreEqual(96u, decoder.PixelHeight);
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TransformHonorsFillWithShrinkOnlyWhenNoChangeRequired()
|
||||
public async Task TransformHonorsFillWithShrinkOnlyWhenNoChangeRequired()
|
||||
{
|
||||
// With a scale of 1.0 on both axes, the original should be returned.
|
||||
var operation = new ResizeOperation(
|
||||
"Test.jpg",
|
||||
_directory,
|
||||
@@ -470,70 +482,70 @@ namespace ImageResizer.Models
|
||||
x.SelectedSize.Height = 96;
|
||||
}));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
AssertEx.Image(
|
||||
await AssertEx.ImageAsync(
|
||||
_directory.File(),
|
||||
image =>
|
||||
decoder =>
|
||||
{
|
||||
Assert.AreEqual(96, image.Frames[0].PixelWidth);
|
||||
Assert.AreEqual(96, image.Frames[0].PixelHeight);
|
||||
Assert.AreEqual(96u, decoder.PixelWidth);
|
||||
Assert.AreEqual(96u, decoder.PixelHeight);
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetDestinationPathUniquifiesOutputFilename()
|
||||
public async Task GetDestinationPathUniquifiesOutputFilename()
|
||||
{
|
||||
File.WriteAllBytes(Path.Combine(_directory, "Test (Test).png"), Array.Empty<byte>());
|
||||
|
||||
var operation = new ResizeOperation("Test.png", _directory, Settings());
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
CollectionAssert.Contains(_directory.FileNames.ToList(), "Test (Test) (1).png");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetDestinationPathUniquifiesOutputFilenameAgain()
|
||||
public async Task GetDestinationPathUniquifiesOutputFilenameAgain()
|
||||
{
|
||||
File.WriteAllBytes(Path.Combine(_directory, "Test (Test).png"), Array.Empty<byte>());
|
||||
File.WriteAllBytes(Path.Combine(_directory, "Test (Test) (1).png"), Array.Empty<byte>());
|
||||
|
||||
var operation = new ResizeOperation("Test.png", _directory, Settings());
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
CollectionAssert.Contains(_directory.FileNames.ToList(), "Test (Test) (2).png");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GetDestinationPathUsesFileNameFormat()
|
||||
public async Task GetDestinationPathUsesFileNameFormat()
|
||||
{
|
||||
var operation = new ResizeOperation(
|
||||
"Test.png",
|
||||
_directory,
|
||||
Settings(s => s.FileName = "%1_%2_%3_%4_%5_%6"));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
CollectionAssert.Contains(_directory.FileNames.ToList(), "Test_Test_96_96_96_48.png");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ExecuteHandlesDirectoriesInFileNameFormat()
|
||||
public async Task ExecuteHandlesDirectoriesInFileNameFormat()
|
||||
{
|
||||
var operation = new ResizeOperation(
|
||||
"Test.png",
|
||||
_directory,
|
||||
Settings(s => s.FileName = @"Directory\%1 (%2)"));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
Assert.IsTrue(File.Exists(_directory + @"\Directory\Test (Test).png"));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void StripMetadata()
|
||||
public async Task StripMetadata()
|
||||
{
|
||||
var operation = new ResizeOperation(
|
||||
"TestMetadataIssue1928.jpg",
|
||||
@@ -544,18 +556,26 @@ namespace ImageResizer.Models
|
||||
x.RemoveMetadata = true;
|
||||
}));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
AssertEx.Image(
|
||||
await AssertEx.ImageAsync(
|
||||
_directory.File(),
|
||||
image => Assert.IsNull(((BitmapMetadata)image.Frames[0].Metadata).DateTaken));
|
||||
AssertEx.Image(
|
||||
_directory.File(),
|
||||
image => Assert.IsNotNull(((BitmapMetadata)image.Frames[0].Metadata).GetQuerySafe("System.Photo.Orientation")));
|
||||
async decoder =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var props = await decoder.BitmapProperties.GetPropertiesAsync(DateTakenPropertyQuery);
|
||||
Assert.IsFalse(props.ContainsKey("System.Photo.DateTaken"), "DateTaken should be stripped");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// If GetPropertiesAsync throws, metadata is not present — which is expected
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void StripMetadataWhenNoMetadataPresent()
|
||||
public async Task StripMetadataWhenNoMetadataPresent()
|
||||
{
|
||||
var operation = new ResizeOperation(
|
||||
"TestMetadataIssue1928_NoMetadata.jpg",
|
||||
@@ -566,18 +586,26 @@ namespace ImageResizer.Models
|
||||
x.RemoveMetadata = true;
|
||||
}));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
AssertEx.Image(
|
||||
await AssertEx.ImageAsync(
|
||||
_directory.File(),
|
||||
image => Assert.IsNull(((BitmapMetadata)image.Frames[0].Metadata).DateTaken));
|
||||
AssertEx.Image(
|
||||
_directory.File(),
|
||||
image => Assert.IsNull(((BitmapMetadata)image.Frames[0].Metadata).GetQuerySafe("System.Photo.Orientation")));
|
||||
async decoder =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var props = await decoder.BitmapProperties.GetPropertiesAsync(DateTakenPropertyQuery);
|
||||
Assert.IsFalse(props.ContainsKey("System.Photo.DateTaken"), "DateTaken should not exist");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Expected: no metadata block at all
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void VerifyFileNameIsSanitized()
|
||||
public async Task VerifyFileNameIsSanitized()
|
||||
{
|
||||
var operation = new ResizeOperation(
|
||||
"Test.png",
|
||||
@@ -589,13 +617,13 @@ namespace ImageResizer.Models
|
||||
s.SelectedSize.Name = "Test\\/";
|
||||
}));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
Assert.IsTrue(File.Exists(_directory + @"\Directory\Test_______(Test__).png"));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void VerifyNotRecommendedNameIsChanged()
|
||||
public async Task VerifyNotRecommendedNameIsChanged()
|
||||
{
|
||||
var operation = new ResizeOperation(
|
||||
"Test.png",
|
||||
@@ -606,7 +634,7 @@ namespace ImageResizer.Models
|
||||
s.FileName = @"nul";
|
||||
}));
|
||||
|
||||
operation.Execute();
|
||||
await operation.ExecuteAsync();
|
||||
|
||||
Assert.IsTrue(File.Exists(_directory + @"\nul_.png"));
|
||||
}
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
|
||||
using ImageResizer.Properties;
|
||||
using ImageResizer.Helpers;
|
||||
using ImageResizer.Test;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
@@ -17,6 +17,12 @@ namespace ImageResizer.Models
|
||||
[TestClass]
|
||||
public class ResizeSizeTests
|
||||
{
|
||||
[ClassInitialize]
|
||||
public static void ClassInit(TestContext context)
|
||||
{
|
||||
ResourceLoaderInstance.GetString = key => key;
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void NameWorks()
|
||||
{
|
||||
@@ -34,22 +40,11 @@ namespace ImageResizer.Models
|
||||
[TestMethod]
|
||||
public void NameReplacesTokens()
|
||||
{
|
||||
var args = new List<(string, string)>
|
||||
{
|
||||
("$small$", Resources.Small),
|
||||
("$medium$", Resources.Medium),
|
||||
("$large$", Resources.Large),
|
||||
("$phone$", Resources.Phone),
|
||||
};
|
||||
foreach (var (name, expected) in args)
|
||||
{
|
||||
var size = new ResizeSize
|
||||
{
|
||||
Name = name,
|
||||
};
|
||||
var size = new ResizeSize();
|
||||
|
||||
Assert.AreEqual(expected, size.Name);
|
||||
}
|
||||
size.Name = "$small$";
|
||||
|
||||
Assert.AreEqual("Small", size.Name);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -57,13 +52,15 @@ namespace ImageResizer.Models
|
||||
{
|
||||
var size = new ResizeSize();
|
||||
|
||||
var e = AssertEx.Raises<PropertyChangedEventArgs>(
|
||||
var events = AssertEx.RaisesAll<PropertyChangedEventArgs>(
|
||||
h => size.PropertyChanged += h,
|
||||
h => size.PropertyChanged -= h,
|
||||
() => size.Fit = ResizeFit.Stretch);
|
||||
|
||||
Assert.AreEqual(ResizeFit.Stretch, size.Fit);
|
||||
Assert.AreEqual(nameof(ResizeSize.Fit), e.Arguments.PropertyName);
|
||||
Assert.IsTrue(
|
||||
events.Any(e => e.Arguments.PropertyName == nameof(ResizeSize.Fit)),
|
||||
"Expected PropertyChanged for Fit");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -135,13 +132,15 @@ namespace ImageResizer.Models
|
||||
{
|
||||
var size = new ResizeSize();
|
||||
|
||||
var e = AssertEx.Raises<PropertyChangedEventArgs>(
|
||||
var events = AssertEx.RaisesAll<PropertyChangedEventArgs>(
|
||||
h => size.PropertyChanged += h,
|
||||
h => size.PropertyChanged -= h,
|
||||
() => size.Unit = ResizeUnit.Inch);
|
||||
|
||||
Assert.AreEqual(ResizeUnit.Inch, size.Unit);
|
||||
Assert.AreEqual(nameof(ResizeSize.Unit), e.Arguments.PropertyName);
|
||||
Assert.IsTrue(
|
||||
events.Any(e => e.Arguments.PropertyName == nameof(ResizeSize.Unit)),
|
||||
"Expected PropertyChanged for Unit");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
||||
@@ -11,6 +11,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
using ImageResizer.Helpers;
|
||||
using ImageResizer.Models;
|
||||
using ImageResizer.Test;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
@@ -25,10 +26,6 @@ namespace ImageResizer.Properties
|
||||
WriteIndented = true,
|
||||
};
|
||||
|
||||
private static readonly CompositeFormat ValueMustBeBetween = System.Text.CompositeFormat.Parse(Properties.Resources.ValueMustBeBetween);
|
||||
|
||||
private static App _imageResizerApp;
|
||||
|
||||
public SettingsTests()
|
||||
{
|
||||
// Change settings.json path to a temp file
|
||||
@@ -38,8 +35,7 @@ namespace ImageResizer.Properties
|
||||
[ClassInitialize]
|
||||
public static void ClassInitialize(TestContext context)
|
||||
{
|
||||
// new App() needs to be created since Settings.Reload() uses App.Current to update properties on the UI thread. App() can be created only once otherwise it results in System.InvalidOperationException : Cannot create more than one System.Windows.Application instance in the same AppDomain.
|
||||
_imageResizerApp = new App();
|
||||
ResourceLoaderInstance.GetString = key => key;
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -193,9 +189,11 @@ namespace ImageResizer.Properties
|
||||
|
||||
var result = ((IDataErrorInfo)settings)["JpegQualityLevel"];
|
||||
|
||||
// Using InvariantCulture since this is used internally
|
||||
// With test ResourceLoaderInstance, GetString returns the key itself ("ValueMustBeBetween")
|
||||
// which becomes the CompositeFormat. Format it the same way the production code does.
|
||||
var expectedFormat = CompositeFormat.Parse(ResourceLoaderInstance.GetString("ValueMustBeBetween"));
|
||||
Assert.AreEqual(
|
||||
string.Format(CultureInfo.InvariantCulture, ValueMustBeBetween, 1, 100),
|
||||
string.Format(CultureInfo.InvariantCulture, expectedFormat, 1, 100),
|
||||
result);
|
||||
}
|
||||
|
||||
@@ -275,7 +273,11 @@ namespace ImageResizer.Properties
|
||||
{
|
||||
// Arrange
|
||||
var settings = new Settings();
|
||||
settings.Save(); // To create the settings file
|
||||
settings.SelectedSizeIndex = 2;
|
||||
settings.Save();
|
||||
|
||||
// Reset to default so Reload will trigger a real change
|
||||
settings.SelectedSizeIndex = 0;
|
||||
|
||||
var shrinkOnlyChanged = false;
|
||||
var replaceChanged = false;
|
||||
@@ -385,8 +387,7 @@ namespace ImageResizer.Properties
|
||||
[ClassCleanup]
|
||||
public static void ClassCleanup()
|
||||
{
|
||||
_imageResizerApp.Dispose();
|
||||
_imageResizerApp = null;
|
||||
// No App instance to dispose in WinUI3 test environment
|
||||
}
|
||||
|
||||
[TestCleanup]
|
||||
|
||||
@@ -8,11 +8,14 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
using Windows.Graphics.Imaging;
|
||||
|
||||
[module: System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1636:FileHeaderCopyrightTextMustMatch", Justification = "File created under PowerToys.")]
|
||||
|
||||
namespace ImageResizer.Test
|
||||
@@ -29,17 +32,20 @@ namespace ImageResizer.Test
|
||||
}
|
||||
}
|
||||
|
||||
public static void Image(string path, Action<BitmapDecoder> action)
|
||||
public static async Task ImageAsync(string path, Action<BitmapDecoder> action)
|
||||
{
|
||||
using (var stream = _fileSystem.File.OpenRead(path))
|
||||
{
|
||||
var image = BitmapDecoder.Create(
|
||||
stream,
|
||||
BitmapCreateOptions.PreservePixelFormat,
|
||||
BitmapCacheOption.None);
|
||||
using var stream = _fileSystem.File.OpenRead(path);
|
||||
var winrtStream = stream.AsRandomAccessStream();
|
||||
var decoder = await BitmapDecoder.CreateAsync(winrtStream);
|
||||
action(decoder);
|
||||
}
|
||||
|
||||
action(image);
|
||||
}
|
||||
public static async Task ImageAsync(string path, Func<BitmapDecoder, Task> action)
|
||||
{
|
||||
using var stream = _fileSystem.File.OpenRead(path);
|
||||
var winrtStream = stream.AsRandomAccessStream();
|
||||
var decoder = await BitmapDecoder.CreateAsync(winrtStream);
|
||||
await action(decoder);
|
||||
}
|
||||
|
||||
public static RaisedEvent<NotifyCollectionChangedEventArgs> Raises<T>(
|
||||
@@ -78,6 +84,24 @@ namespace ImageResizer.Test
|
||||
return raisedEvent;
|
||||
}
|
||||
|
||||
public static IList<RaisedEvent<PropertyChangedEventArgs>> RaisesAll<T>(
|
||||
Action<PropertyChangedEventHandler> attach,
|
||||
Action<PropertyChangedEventHandler> detach,
|
||||
Action testCode)
|
||||
where T : PropertyChangedEventArgs
|
||||
{
|
||||
var events = new List<RaisedEvent<PropertyChangedEventArgs>>();
|
||||
PropertyChangedEventHandler handler = (sender, e)
|
||||
=> events.Add(new RaisedEvent<PropertyChangedEventArgs>(sender, e));
|
||||
attach(handler);
|
||||
testCode();
|
||||
detach(handler);
|
||||
|
||||
Assert.IsTrue(events.Count > 0, "Expected at least one PropertyChanged event.");
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
public sealed class RaisedEvent<TArgs>
|
||||
{
|
||||
public RaisedEvent(object sender, TArgs args)
|
||||
|
||||
@@ -1,28 +1,34 @@
|
||||
#pragma warning disable IDE0073
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.Storage.Streams;
|
||||
|
||||
namespace ImageResizer.Test
|
||||
{
|
||||
internal static class BitmapSourceExtensions
|
||||
{
|
||||
public static Color GetFirstPixel(this BitmapSource source)
|
||||
public static async Task<(byte R, byte G, byte B, byte A)> GetFirstPixelAsync(this BitmapDecoder decoder)
|
||||
{
|
||||
var pixel = new byte[4];
|
||||
new FormatConvertedBitmap(
|
||||
new CroppedBitmap(source, new Int32Rect(0, 0, 1, 1)),
|
||||
PixelFormats.Bgra32,
|
||||
destinationPalette: null,
|
||||
alphaThreshold: 0)
|
||||
.CopyPixels(pixel, 4, 0);
|
||||
using var softwareBitmap = await decoder.GetSoftwareBitmapAsync(
|
||||
BitmapPixelFormat.Bgra8,
|
||||
BitmapAlphaMode.Premultiplied);
|
||||
|
||||
return Color.FromArgb(pixel[3], pixel[2], pixel[1], pixel[0]);
|
||||
var buffer = new Windows.Storage.Streams.Buffer((uint)(softwareBitmap.PixelWidth * softwareBitmap.PixelHeight * 4));
|
||||
softwareBitmap.CopyToBuffer(buffer);
|
||||
|
||||
using var reader = DataReader.FromBuffer(buffer);
|
||||
byte b = reader.ReadByte();
|
||||
byte g = reader.ReadByte();
|
||||
byte r = reader.ReadByte();
|
||||
byte a = reader.ReadByte();
|
||||
|
||||
return (r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
using ImageResizer.Properties;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace ImageResizer.Views
|
||||
{
|
||||
public class TimeRemainingConverterTests
|
||||
{
|
||||
[DataTestMethod]
|
||||
[DataRow("HourMinute", 1, 1, 0)]
|
||||
[DataRow("HourMinutes", 1, 2, 0)]
|
||||
[DataRow("HoursMinute", 2, 1, 0)]
|
||||
[DataRow("HoursMinutes", 2, 2, 0)]
|
||||
[DataRow("MinuteSecond", 0, 1, 1)]
|
||||
[DataRow("MinuteSeconds", 0, 1, 2)]
|
||||
[DataRow("MinutesSecond", 0, 2, 1)]
|
||||
[DataRow("MinutesSeconds", 0, 2, 2)]
|
||||
[DataRow("Second", 0, 0, 1)]
|
||||
[DataRow("Seconds", 0, 0, 2)]
|
||||
public void ConvertWorks(string resource, int hours, int minutes, int seconds)
|
||||
{
|
||||
var timeRemaining = new TimeSpan(hours, minutes, seconds);
|
||||
var converter = new TimeRemainingConverter();
|
||||
|
||||
// Using InvariantCulture since these are internal
|
||||
var result = converter.Convert(
|
||||
timeRemaining,
|
||||
targetType: null,
|
||||
parameter: null,
|
||||
CultureInfo.InvariantCulture);
|
||||
|
||||
Assert.AreEqual(
|
||||
string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
Resources.ResourceManager.GetString("Progress_TimeRemaining_" + resource, CultureInfo.InvariantCulture),
|
||||
hours,
|
||||
minutes,
|
||||
seconds),
|
||||
result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
<Application
|
||||
x:Class="ImageResizer.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:m="clr-namespace:ImageResizer.Models"
|
||||
xmlns:sys="clr-namespace:System;assembly=System.Runtime"
|
||||
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
|
||||
xmlns:v="clr-namespace:ImageResizer.Views">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ui:ThemesDictionary Theme="Dark" />
|
||||
<ui:ControlsDictionary />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<v:SizeTypeToVisibilityConverter x:Key="SizeTypeToVisibilityConverter" />
|
||||
<v:SizeTypeToHelpTextConverter x:Key="SizeTypeToHelpTextConverter" />
|
||||
<v:EnumValueConverter x:Key="EnumValueConverter" />
|
||||
<v:AutoDoubleConverter x:Key="AutoDoubleConverter" />
|
||||
<v:BoolValueConverter x:Key="BoolValueConverter" />
|
||||
<v:VisibilityBoolConverter x:Key="VisibilityBoolConverter" />
|
||||
<v:EnumToIntConverter x:Key="EnumToIntConverter" />
|
||||
<v:AccessTextToTextConverter x:Key="AccessTextToTextConverter" />
|
||||
<v:NumberBoxValueConverter x:Key="NumberBoxValueConverter" />
|
||||
<v:ZeroToEmptyStringNumberFormatter x:Key="ZeroToEmptyStringNumberFormatter" />
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
@@ -1,243 +0,0 @@
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using ImageResizer.Models;
|
||||
using ImageResizer.Properties;
|
||||
using ImageResizer.Utilities;
|
||||
using ImageResizer.ViewModels;
|
||||
using ImageResizer.Views;
|
||||
using ManagedCommon;
|
||||
|
||||
namespace ImageResizer
|
||||
{
|
||||
public partial class App : Application, IDisposable
|
||||
{
|
||||
private const string LogSubFolder = "\\Image Resizer\\Logs";
|
||||
|
||||
/// <summary>
|
||||
/// Gets cached AI availability state, checked at app startup.
|
||||
/// Can be updated after model download completes or background initialization.
|
||||
/// </summary>
|
||||
public static AiAvailabilityState AiAvailabilityState { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when AI initialization completes in background.
|
||||
/// Allows UI to refresh state when initialization finishes.
|
||||
/// </summary>
|
||||
public static event EventHandler<AiAvailabilityState> AiInitializationCompleted;
|
||||
|
||||
static App()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Initialize logger early (mirroring PowerOCR pattern)
|
||||
Logger.InitializeLogger(LogSubFolder);
|
||||
}
|
||||
catch
|
||||
{
|
||||
/* swallow logger init issues silently */
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string appLanguage = LanguageHelper.LoadLanguage();
|
||||
if (!string.IsNullOrEmpty(appLanguage))
|
||||
{
|
||||
System.Threading.Thread.CurrentThread.CurrentUICulture = new CultureInfo(appLanguage);
|
||||
}
|
||||
}
|
||||
catch (CultureNotFoundException ex)
|
||||
{
|
||||
Logger.LogError("CultureNotFoundException: " + ex.Message);
|
||||
}
|
||||
|
||||
Console.InputEncoding = Encoding.Unicode;
|
||||
}
|
||||
|
||||
protected override void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
// Fix for .net 3.1.19 making Image Resizer not adapt to DPI changes.
|
||||
NativeMethods.SetProcessDPIAware();
|
||||
|
||||
// TODO: Re-enable AI Super Resolution in next release by removing this #if block
|
||||
// Temporarily disable AI Super Resolution feature (hide from UI but keep code)
|
||||
#if true // Set to false to re-enable AI Super Resolution
|
||||
AiAvailabilityState = AiAvailabilityState.NotSupported;
|
||||
ResizeBatch.SetAiSuperResolutionService(Services.NoOpAiSuperResolutionService.Instance);
|
||||
|
||||
// Skip AI detection mode as well
|
||||
if (e?.Args?.Length > 0 && e.Args[0] == "--detect-ai")
|
||||
{
|
||||
Services.AiAvailabilityCacheService.SaveCache(AiAvailabilityState.NotSupported);
|
||||
Environment.Exit(0);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
// Check for AI detection mode (called by Runner in background)
|
||||
if (e?.Args?.Length > 0 && e.Args[0] == "--detect-ai")
|
||||
{
|
||||
RunAiDetectionMode();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (PowerToys.GPOWrapperProjection.GPOWrapper.GetConfiguredImageResizerEnabledValue() == PowerToys.GPOWrapperProjection.GpoRuleConfigured.Disabled)
|
||||
{
|
||||
/* TODO: Add logs to ImageResizer.
|
||||
* Logger.LogWarning("Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator.");
|
||||
*/
|
||||
Logger.LogWarning("GPO policy disables ImageResizer. Exiting.");
|
||||
Environment.Exit(0); // Current.Exit won't work until there's a window opened.
|
||||
return;
|
||||
}
|
||||
|
||||
// AI Super Resolution is not supported on Windows 10 - skip cache check entirely
|
||||
if (OSVersionHelper.IsWindows10())
|
||||
{
|
||||
AiAvailabilityState = AiAvailabilityState.NotSupported;
|
||||
ResizeBatch.SetAiSuperResolutionService(Services.NoOpAiSuperResolutionService.Instance);
|
||||
Logger.LogInfo("AI Super Resolution not supported on Windows 10");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Load AI availability from cache (written by Runner's background detection)
|
||||
var cachedState = Services.AiAvailabilityCacheService.LoadCache();
|
||||
|
||||
if (cachedState.HasValue)
|
||||
{
|
||||
AiAvailabilityState = cachedState.Value;
|
||||
Logger.LogInfo($"AI state loaded from cache: {AiAvailabilityState}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// No valid cache - default to NotSupported (Runner will detect and cache for next startup)
|
||||
AiAvailabilityState = AiAvailabilityState.NotSupported;
|
||||
Logger.LogInfo("No AI cache found, defaulting to NotSupported");
|
||||
}
|
||||
|
||||
// If AI is potentially available, start background initialization (non-blocking)
|
||||
if (AiAvailabilityState == AiAvailabilityState.Ready)
|
||||
{
|
||||
_ = InitializeAiServiceAsync(); // Fire and forget - don't block UI
|
||||
}
|
||||
else
|
||||
{
|
||||
// AI not available - set NoOp service immediately
|
||||
ResizeBatch.SetAiSuperResolutionService(Services.NoOpAiSuperResolutionService.Instance);
|
||||
}
|
||||
}
|
||||
|
||||
var batch = ResizeBatch.FromCommandLine(Console.In, e?.Args);
|
||||
|
||||
// TODO: Add command-line parameters that can be used in lieu of the input page (issue #14)
|
||||
var mainWindow = new MainWindow(new MainViewModel(batch, Settings.Default));
|
||||
mainWindow.Show();
|
||||
|
||||
// Temporary workaround for issue #1273
|
||||
WindowHelpers.BringToForeground(new System.Windows.Interop.WindowInteropHelper(mainWindow).Handle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AI detection mode: perform detection, write to cache, and exit.
|
||||
/// Called by Runner in background to avoid blocking ImageResizer UI startup.
|
||||
/// </summary>
|
||||
private void RunAiDetectionMode()
|
||||
{
|
||||
try
|
||||
{
|
||||
Logger.LogInfo("Running AI detection mode...");
|
||||
|
||||
// AI Super Resolution is not supported on Windows 10
|
||||
if (OSVersionHelper.IsWindows10())
|
||||
{
|
||||
Logger.LogInfo("AI detection skipped: Windows 10 does not support AI Super Resolution");
|
||||
Services.AiAvailabilityCacheService.SaveCache(AiAvailabilityState.NotSupported);
|
||||
Environment.Exit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform detection (reuse existing logic)
|
||||
var state = CheckAiAvailability();
|
||||
|
||||
// Write result to cache file
|
||||
Services.AiAvailabilityCacheService.SaveCache(state);
|
||||
|
||||
Logger.LogInfo($"AI detection complete: {state}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"AI detection failed: {ex.Message}");
|
||||
Services.AiAvailabilityCacheService.SaveCache(AiAvailabilityState.NotSupported);
|
||||
}
|
||||
|
||||
// Exit silently without showing UI
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check AI Super Resolution availability on this system.
|
||||
/// Performs architecture check and model availability check.
|
||||
/// </summary>
|
||||
private static AiAvailabilityState CheckAiAvailability()
|
||||
{
|
||||
// AI feature disabled - always return NotSupported
|
||||
return AiAvailabilityState.NotSupported;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize AI Super Resolution service asynchronously in background.
|
||||
/// Runs without blocking UI startup - state change event notifies completion.
|
||||
/// </summary>
|
||||
private static async System.Threading.Tasks.Task InitializeAiServiceAsync()
|
||||
{
|
||||
AiAvailabilityState finalState;
|
||||
|
||||
try
|
||||
{
|
||||
// Create and initialize AI service using async factory
|
||||
var aiService = await Services.WinAiSuperResolutionService.CreateAsync();
|
||||
|
||||
if (aiService != null)
|
||||
{
|
||||
ResizeBatch.SetAiSuperResolutionService(aiService);
|
||||
Logger.LogInfo("AI Super Resolution service initialized successfully.");
|
||||
finalState = AiAvailabilityState.Ready;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Initialization failed - use default NoOp service
|
||||
ResizeBatch.SetAiSuperResolutionService(Services.NoOpAiSuperResolutionService.Instance);
|
||||
Logger.LogWarning("AI Super Resolution service initialization failed. Using default service.");
|
||||
finalState = AiAvailabilityState.NotSupported;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Log error and use default NoOp service
|
||||
ResizeBatch.SetAiSuperResolutionService(Services.NoOpAiSuperResolutionService.Instance);
|
||||
Logger.LogError($"Exception during AI service initialization: {ex.Message}");
|
||||
finalState = AiAvailabilityState.NotSupported;
|
||||
}
|
||||
|
||||
// Update cached state and notify listeners
|
||||
AiAvailabilityState = finalState;
|
||||
AiInitializationCompleted?.Invoke(null, finalState);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Dispose AI Super Resolution service
|
||||
ResizeBatch.DisposeAiSuperResolutionService();
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
@@ -6,6 +6,7 @@ using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using ImageResizer.Models;
|
||||
using ImageResizer.Properties;
|
||||
@@ -58,10 +59,10 @@ namespace ImageResizer.Cli
|
||||
return 1;
|
||||
}
|
||||
|
||||
return RunSilentMode(cliOptions);
|
||||
return RunSilentModeAsync(cliOptions).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
private int RunSilentMode(CliOptions cliOptions)
|
||||
private async Task<int> RunSilentModeAsync(CliOptions cliOptions)
|
||||
{
|
||||
var batch = ResizeBatch.FromCliOptions(Console.In, cliOptions);
|
||||
var settings = Settings.Default;
|
||||
@@ -73,7 +74,7 @@ namespace ImageResizer.Cli
|
||||
bool useLineBasedProgress = cliOptions.ProgressLines ?? false;
|
||||
int lastReportedMilestone = -1;
|
||||
|
||||
var errors = batch.Process(
|
||||
var errors = await batch.ProcessAsync(
|
||||
(completed, total) =>
|
||||
{
|
||||
var progress = (int)((completed / total) * 100);
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// 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 ImageResizer.Helpers;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
|
||||
namespace ImageResizer.Converters
|
||||
{
|
||||
public partial class AutoDoubleConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is double d && (d == 0 || double.IsNaN(d)))
|
||||
{
|
||||
return ResourceLoaderInstance.GetString("Auto");
|
||||
}
|
||||
|
||||
return value?.ToString();
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
src/modules/imageresizer/ui/Converters/EnumToIntConverter.cs
Normal file
32
src/modules/imageresizer/ui/Converters/EnumToIntConverter.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
// 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 Microsoft.UI.Xaml.Data;
|
||||
|
||||
namespace ImageResizer.Converters
|
||||
{
|
||||
public partial class EnumToIntConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is Enum)
|
||||
{
|
||||
return System.Convert.ToInt32(value);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is int intValue && targetType.IsEnum)
|
||||
{
|
||||
return Enum.ToObject(targetType, intValue);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,24 @@
|
||||
#pragma warning disable IDE0073
|
||||
#pragma warning disable IDE0073, SA1636
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
// Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
#pragma warning restore IDE0073, SA1636
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Windows.Data;
|
||||
using ImageResizer.Helpers;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
|
||||
using ImageResizer.Properties;
|
||||
|
||||
namespace ImageResizer.Views
|
||||
namespace ImageResizer.Converters
|
||||
{
|
||||
[ValueConversion(typeof(Enum), typeof(string))]
|
||||
public class EnumValueConverter : IValueConverter
|
||||
public partial class EnumValueConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
var type = value?.GetType();
|
||||
if (!type.IsEnum)
|
||||
if (type == null || !type.IsEnum)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
@@ -44,20 +42,18 @@ namespace ImageResizer.Views
|
||||
.Append(parameter);
|
||||
}
|
||||
|
||||
// Fixes #16792 - Looks like culture defaults to en-US, so wrong resource is being fetched.
|
||||
#pragma warning disable CA1304 // Specify CultureInfo
|
||||
var targetValue = Resources.ResourceManager.GetString(builder.ToString());
|
||||
#pragma warning restore CA1304 // Specify CultureInfo
|
||||
var targetValue = ResourceLoaderInstance.GetString(builder.ToString());
|
||||
|
||||
if (toLower)
|
||||
if (toLower && !string.IsNullOrEmpty(targetValue))
|
||||
{
|
||||
var culture = string.IsNullOrEmpty(language) ? CultureInfo.CurrentCulture : new CultureInfo(language);
|
||||
targetValue = targetValue.ToLower(culture);
|
||||
}
|
||||
|
||||
return targetValue;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
=> value;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
namespace System.Windows.Media.Imaging
|
||||
{
|
||||
internal static class BitmapEncoderExtensions
|
||||
{
|
||||
public static bool CanEncode(this BitmapEncoder encoder)
|
||||
{
|
||||
try
|
||||
{
|
||||
var temp = encoder.CodecInfo;
|
||||
}
|
||||
catch (NotSupportedException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace ImageResizer.Extensions
|
||||
{
|
||||
internal static class BitmapMetadataExtension
|
||||
{
|
||||
public static void CopyMetadataPropertyTo(this BitmapMetadata source, BitmapMetadata target, string query)
|
||||
{
|
||||
if (source == null || target == null || string.IsNullOrWhiteSpace(query))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var value = source.GetQuerySafe(query);
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
target.SetQuery(query, value);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// InvalidOperationException is thrown if metadata object is in readonly state.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public static object GetQuerySafe(this BitmapMetadata metadata, string query)
|
||||
{
|
||||
if (metadata == null || string.IsNullOrWhiteSpace(query))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (metadata.ContainsQuery(query))
|
||||
{
|
||||
return metadata.GetQuery(query);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (NotSupportedException)
|
||||
{
|
||||
// NotSupportedException is throw if the metadata entry is not preset on the target image (e.g. Orientation not set).
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void RemoveQuerySafe(this BitmapMetadata metadata, string query)
|
||||
{
|
||||
if (metadata == null || string.IsNullOrWhiteSpace(query))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (metadata.ContainsQuery(query))
|
||||
{
|
||||
metadata.RemoveQuery(query);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Exception while trying to remove metadata entry at position: {query}");
|
||||
Debug.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetQuerySafe(this BitmapMetadata metadata, string query, object value)
|
||||
{
|
||||
if (metadata == null || string.IsNullOrWhiteSpace(query) || value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
metadata.SetQuery(query, value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Exception while trying to set metadata {value} at position: {query}");
|
||||
Debug.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all metadata.
|
||||
/// Iterates recursively through metadata and adds valid items to a list while skipping invalid data items.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Invalid data items are items which throw an exception when reading the data with metadata.GetQuery(...).
|
||||
/// Sometimes Metadata collections are improper closed and cause an exception on IEnumerator.MoveNext(). In this case, we return all data items which were successfully collected so far.
|
||||
/// </remarks>
|
||||
/// <returns>
|
||||
/// metadata path and metadata value of all successfully read data items.
|
||||
/// </returns>
|
||||
public static List<(string MetadataPath, object Value)> GetListOfMetadata(this BitmapMetadata metadata)
|
||||
{
|
||||
var listOfAllMetadata = new List<(string MetadataPath, object Value)>();
|
||||
|
||||
try
|
||||
{
|
||||
GetMetadataRecursively(metadata, string.Empty);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Exception while trying to iterate recursively over metadata. We were able to read {listOfAllMetadata.Count} metadata entries.");
|
||||
Debug.WriteLine(ex);
|
||||
}
|
||||
|
||||
return listOfAllMetadata;
|
||||
|
||||
void GetMetadataRecursively(BitmapMetadata metadata, string query)
|
||||
{
|
||||
foreach (string relativeQuery in metadata)
|
||||
{
|
||||
string absolutePath = query + relativeQuery;
|
||||
|
||||
object metadataQueryReader = null;
|
||||
|
||||
try
|
||||
{
|
||||
metadataQueryReader = GetQueryWithPreCheck(metadata, relativeQuery);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Removing corrupt metadata property {absolutePath}. Skipping metadata entry | {ex.Message}");
|
||||
Debug.WriteLine(ex);
|
||||
}
|
||||
|
||||
if (metadataQueryReader != null)
|
||||
{
|
||||
listOfAllMetadata.Add((absolutePath, metadataQueryReader));
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.WriteLine($"No metadata found for query {absolutePath}. Skipping empty null entry because its invalid.");
|
||||
}
|
||||
|
||||
if (metadataQueryReader is BitmapMetadata innerMetadata)
|
||||
{
|
||||
GetMetadataRecursively(innerMetadata, absolutePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object GetQueryWithPreCheck(BitmapMetadata metadata, string query)
|
||||
{
|
||||
if (metadata == null || string.IsNullOrWhiteSpace(query))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (metadata.ContainsQuery(query))
|
||||
{
|
||||
return metadata.GetQuery(query);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prints all metadata to debug console
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Intended for debug only!!!
|
||||
/// </remarks>
|
||||
public static void PrintsAllMetadataToDebugOutput(this BitmapMetadata metadata)
|
||||
{
|
||||
if (metadata == null)
|
||||
{
|
||||
Debug.WriteLine($"Metadata was null.");
|
||||
}
|
||||
|
||||
var listOfMetadata = metadata.GetListOfMetadataForDebug();
|
||||
foreach (var metadataItem in listOfMetadata)
|
||||
{
|
||||
// Debug.WriteLine($"modifiableMetadata.RemoveQuerySafe(\"{metadataItem.metadataPath}\");");
|
||||
Debug.WriteLine($"{metadataItem.MetadataPath} | {metadataItem.Value}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all metadata
|
||||
/// Iterates recursively through all metadata
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Intended for debug only!!!
|
||||
/// </remarks>
|
||||
public static List<(string MetadataPath, object Value)> GetListOfMetadataForDebug(this BitmapMetadata metadata)
|
||||
{
|
||||
var listOfAllMetadata = new List<(string MetadataPath, object Value)>();
|
||||
|
||||
try
|
||||
{
|
||||
GetMetadataRecursively(metadata, string.Empty);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Exception while trying to iterate recursively over metadata. We were able to read {listOfAllMetadata.Count} metadata entries.");
|
||||
Debug.WriteLine(ex);
|
||||
}
|
||||
|
||||
return listOfAllMetadata;
|
||||
|
||||
void GetMetadataRecursively(BitmapMetadata metadata, string query)
|
||||
{
|
||||
if (metadata == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (string relativeQuery in metadata)
|
||||
{
|
||||
string absolutePath = query + relativeQuery;
|
||||
|
||||
object metadataQueryReader = null;
|
||||
|
||||
try
|
||||
{
|
||||
metadataQueryReader = metadata.GetQuerySafe(relativeQuery);
|
||||
listOfAllMetadata.Add((absolutePath, metadataQueryReader));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
listOfAllMetadata.Add((absolutePath, $"######## INVALID METADATA: {ex.Message}"));
|
||||
Debug.WriteLine(ex);
|
||||
}
|
||||
|
||||
if (metadataQueryReader is BitmapMetadata innerMetadata)
|
||||
{
|
||||
GetMetadataRecursively(innerMetadata, absolutePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma warning disable IDE0073
|
||||
#pragma warning disable IDE0073, SA1636
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
#pragma warning restore IDE0073, SA1636
|
||||
|
||||
namespace System.Collections.Generic
|
||||
{
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
namespace System
|
||||
{
|
||||
internal static class TimeSpanExtensions
|
||||
{
|
||||
public static TimeSpan Multiply(this TimeSpan timeSpan, double scalar)
|
||||
=> new TimeSpan((long)(timeSpan.Ticks * scalar));
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace ImageResizer.Helpers
|
||||
{
|
||||
public class Observable : INotifyPropertyChanged
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected void Set<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
|
||||
{
|
||||
if (Equals(storage, value))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
storage = value;
|
||||
OnPropertyChanged(propertyName);
|
||||
}
|
||||
|
||||
protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace ImageResizer.Helpers
|
||||
{
|
||||
public class RelayCommand : ICommand
|
||||
{
|
||||
private readonly Action _execute;
|
||||
private readonly Func<bool> _canExecute;
|
||||
|
||||
public event EventHandler CanExecuteChanged;
|
||||
|
||||
public RelayCommand(Action execute)
|
||||
: this(execute, null)
|
||||
{
|
||||
}
|
||||
|
||||
public RelayCommand(Action execute, Func<bool> canExecute)
|
||||
{
|
||||
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
|
||||
_canExecute = canExecute;
|
||||
}
|
||||
|
||||
public bool CanExecute(object parameter) => _canExecute == null || _canExecute();
|
||||
|
||||
public void Execute(object parameter) => _execute();
|
||||
|
||||
public void OnCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "abstract T and abstract")]
|
||||
public class RelayCommand<T> : ICommand
|
||||
{
|
||||
private readonly Action<T> execute;
|
||||
|
||||
private readonly Func<T, bool> canExecute;
|
||||
|
||||
public event EventHandler CanExecuteChanged;
|
||||
|
||||
public RelayCommand(Action<T> execute)
|
||||
: this(execute, null)
|
||||
{
|
||||
}
|
||||
|
||||
public RelayCommand(Action<T> execute, Func<T, bool> canExecute)
|
||||
{
|
||||
this.execute = execute ?? throw new ArgumentNullException(nameof(execute));
|
||||
this.canExecute = canExecute;
|
||||
}
|
||||
|
||||
public bool CanExecute(object parameter) => canExecute == null || canExecute((T)parameter);
|
||||
|
||||
public void Execute(object parameter) => execute((T)parameter);
|
||||
|
||||
public void OnCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// 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 Microsoft.Windows.ApplicationModel.Resources;
|
||||
|
||||
namespace ImageResizer.Helpers
|
||||
{
|
||||
internal static class ResourceLoaderInstance
|
||||
{
|
||||
private static Func<string, string> _getString;
|
||||
|
||||
internal static Func<string, string> GetString
|
||||
{
|
||||
get => _getString ??= CreateDefault();
|
||||
set => _getString = value;
|
||||
}
|
||||
|
||||
private static Func<string, string> CreateDefault()
|
||||
{
|
||||
var loader = new ResourceLoader("PowerToys.ImageResizer.pri");
|
||||
return loader.GetString;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,81 +2,92 @@
|
||||
<!-- Look at Directory.Build.props in root for common stuff as well -->
|
||||
<Import Project="$(RepoRoot)src\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="$(RepoRoot)src\Common.SelfContained.props" />
|
||||
|
||||
|
||||
<PropertyGroup>
|
||||
<AssemblyTitle>PowerToys.ImageResizer</AssemblyTitle>
|
||||
<OutputPath>$(RepoRoot)$(Platform)\$(Configuration)\WinUI3Apps\</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
|
||||
<UseWPF>true</UseWPF>
|
||||
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<ProjectGuid>{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}</ProjectGuid>
|
||||
<AssemblyDescription>PowerToys Image Resizer</AssemblyDescription>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps</OutputPath>
|
||||
<RootNamespace>ImageResizer</RootNamespace>
|
||||
<AssemblyName>PowerToys.ImageResizer</AssemblyName>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
|
||||
<WindowsPackageType>None</WindowsPackageType>
|
||||
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||
<ApplicationIcon>Assets\ImageResizer\ImageResizer.ico</ApplicationIcon>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<NoWarn>CA1863</NoWarn>
|
||||
<!-- MRT from windows app sdk will search for a pri file with the same name of the module before defaulting to resources.pri -->
|
||||
<ProjectPriFileName>PowerToys.ImageResizer.pri</ProjectPriFileName>
|
||||
<!-- Custom Main entry point -->
|
||||
<DefineConstants>DISABLE_XAML_GENERATED_MAIN,TRACE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<Page Remove="ImageResizerXAML\App.xaml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="ImageResizerXAML\App.xaml" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>Resources\ImageResizer.ico</ApplicationIcon>
|
||||
<NoWarn>0436;SA1210;SA1516;CA1305;CA1863;CA1852</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- <PropertyGroup>
|
||||
<ApplicationManifest>ImageResizerUI.dev.manifest</ApplicationManifest>
|
||||
<!-- Allow test project to access internal types -->
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="ImageResizer.Test" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Assets\ImageResizer\ImageResizer.ico" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- See https://learn.microsoft.com/windows/apps/develop/platform/csharp-winrt/net-projection-from-cppwinrt-component for more info -->
|
||||
<PropertyGroup>
|
||||
<CsWinRTIncludes>PowerToys.GPOWrapper</CsWinRTIncludes>
|
||||
<CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
|
||||
<ErrorOnDuplicatePublishOutputFiles>false</ErrorOnDuplicatePublishOutputFiles>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(CIBuild)'=='true'">
|
||||
<ApplicationManifest>ImageResizerUI.prod.manifest</ApplicationManifest>
|
||||
</PropertyGroup> -->
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\ImageResizer.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="Resources\ImageResizer.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Converters" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Extensions" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK.AI" />
|
||||
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" />
|
||||
<PackageReference Include="Microsoft.Xaml.Behaviors.WinUI.Managed" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" />
|
||||
<PackageReference Include="System.CommandLine" />
|
||||
<PackageReference Include="System.IO.Abstractions" />
|
||||
<PackageReference Include="WPF-UI" />
|
||||
<PackageReference Include="WinUIEx" />
|
||||
|
||||
<!-- This line forces the WebView2 version used by Windows App SDK to be the one we expect from Directory.Packages.props . -->
|
||||
<PackageReference Include="Microsoft.Web.WebView2" />
|
||||
<!-- HACK: CmdPal uses CommunityToolkit.Common directly. Align the version. -->
|
||||
<PackageReference Include="CommunityToolkit.Common" />
|
||||
<Manifest Include="$(ApplicationManifest)" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Defining the "Msix" ProjectCapability here allows the Single-project MSIX Packaging
|
||||
Tools extension to be activated for this project even if the Windows App SDK Nuget
|
||||
package has not yet been restored -->
|
||||
<ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnablePreviewMsixTooling)'=='true'">
|
||||
<ProjectCapability Include="Msix" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\common\GPOWrapperProjection\GPOWrapperProjection.csproj" />
|
||||
<ProjectReference Include="..\..\..\common\interop\PowerToys.Interop.vcxproj" />
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- HACK: Common.UI is referenced, even if it is not used, to force dll versions to be the same as in other projects that use it. It's still unclear why this is the case, but this is need for flattening the install directory. -->
|
||||
<ProjectReference Include="..\..\..\common\Common.UI\Common.UI.csproj" />
|
||||
<ProjectReference Include="..\..\..\common\GPOWrapper\GPOWrapper.vcxproj" />
|
||||
<ProjectReference Include="..\..\..\common\interop\PowerToys.Interop.vcxproj" />
|
||||
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Ensure Resources directory and ImageResizer.png are available for dependent projects -->
|
||||
<Target Name="CopyResourcesToSharedLocation" AfterTargets="Build">
|
||||
<ItemGroup>
|
||||
<ResourceFiles Include="$(MSBuildProjectDirectory)\Resources\ImageResizer.png" />
|
||||
</ItemGroup>
|
||||
<MakeDir Directories="$(OutputPath)Resources" Condition="!Exists('$(OutputPath)Resources')" />
|
||||
<Copy SourceFiles="@(ResourceFiles)" DestinationFolder="$(OutputPath)Resources" SkipUnchangedFiles="true" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="PowerToys.ImageResizer.app" />
|
||||
<msix xmlns="urn:schemas-microsoft-com:msix.v1"
|
||||
publisher="CN=PowerToys Dev, O=PowerToys, L=Redmond, S=Washington, C=US"
|
||||
packageName="Microsoft.PowerToys.SparseApp"
|
||||
applicationId="PowerToys.ImageResizerUI" />
|
||||
</assembly>
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="PowerToys.ImageResizer.app" />
|
||||
<msix xmlns="urn:schemas-microsoft-com:msix.v1"
|
||||
publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US"
|
||||
packageName="Microsoft.PowerToys.SparseApp"
|
||||
applicationId="PowerToys.ImageResizerUI" />
|
||||
</assembly>
|
||||
33
src/modules/imageresizer/ui/ImageResizerXAML/App.xaml
Normal file
33
src/modules/imageresizer/ui/ImageResizerXAML/App.xaml
Normal file
@@ -0,0 +1,33 @@
|
||||
<Application
|
||||
x:Class="ImageResizer.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="using:ImageResizer.Converters"
|
||||
xmlns:local="using:ImageResizer"
|
||||
xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<!-- Layout tokens -->
|
||||
<x:Double x:Key="ButtonMinWidth">76</x:Double>
|
||||
<Thickness x:Key="PageSectionMargin">12,12,12,0</Thickness>
|
||||
|
||||
<!-- Converters -->
|
||||
<converters:AutoDoubleConverter x:Key="AutoDoubleConverter" />
|
||||
<tkconverters:BoolToVisibilityConverter
|
||||
x:Key="BoolToVisibilityConverter"
|
||||
FalseValue="Collapsed"
|
||||
TrueValue="Visible" />
|
||||
<converters:EnumToIntConverter x:Key="EnumToIntConverter" />
|
||||
<converters:EnumValueConverter x:Key="EnumValueConverter" />
|
||||
|
||||
<tkconverters:BoolToVisibilityConverter
|
||||
x:Key="InvertedBoolToVisibilityConverter"
|
||||
FalseValue="Visible"
|
||||
TrueValue="Collapsed" />
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
244
src/modules/imageresizer/ui/ImageResizerXAML/App.xaml.cs
Normal file
244
src/modules/imageresizer/ui/ImageResizerXAML/App.xaml.cs
Normal file
@@ -0,0 +1,244 @@
|
||||
// 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.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ImageResizer.Models;
|
||||
using ImageResizer.Properties;
|
||||
using ImageResizer.Services;
|
||||
using ImageResizer.ViewModels;
|
||||
using ManagedCommon;
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
namespace ImageResizer
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides application-specific behavior to supplement the default Application class.
|
||||
/// </summary>
|
||||
public partial class App : Application, IDisposable
|
||||
{
|
||||
private const string LogSubFolder = "\\Image Resizer\\Logs";
|
||||
|
||||
/// <summary>
|
||||
/// Gets cached AI availability state, checked at app startup.
|
||||
/// Can be updated after model download completes or background initialization.
|
||||
/// </summary>
|
||||
public static AiAvailabilityState AiAvailabilityState { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when AI initialization completes in background.
|
||||
/// Allows UI to refresh state when initialization finishes.
|
||||
/// </summary>
|
||||
public static event EventHandler<AiAvailabilityState> AiInitializationCompleted;
|
||||
|
||||
private Window _window;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="App"/> class.
|
||||
/// </summary>
|
||||
public App()
|
||||
{
|
||||
try
|
||||
{
|
||||
string appLanguage = LanguageHelper.LoadLanguage();
|
||||
if (!string.IsNullOrEmpty(appLanguage))
|
||||
{
|
||||
Microsoft.Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = appLanguage;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Language initialization error: " + ex.Message);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Logger.InitializeLogger(LogSubFolder);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Swallow logger init issues silently
|
||||
}
|
||||
|
||||
Console.InputEncoding = Encoding.Unicode;
|
||||
|
||||
this.InitializeComponent();
|
||||
|
||||
UnhandledException += App_UnhandledException;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the application is launched normally by the end user.
|
||||
/// </summary>
|
||||
/// <param name="args">Details about the launch request and process.</param>
|
||||
protected override void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
// Initialize dispatcher for cross-thread property change notifications
|
||||
Settings.InitializeDispatcher();
|
||||
|
||||
// Check GPO policy
|
||||
if (PowerToys.GPOWrapper.GPOWrapper.GetConfiguredImageResizerEnabledValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled)
|
||||
{
|
||||
Logger.LogWarning("Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator.");
|
||||
Environment.Exit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for AI detection mode (called by Runner in background)
|
||||
var commandLineArgs = Environment.GetCommandLineArgs();
|
||||
if (commandLineArgs?.Length > 1 && commandLineArgs[1] == "--detect-ai")
|
||||
{
|
||||
RunAiDetectionMode();
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize AI availability
|
||||
InitializeAiAvailability();
|
||||
|
||||
// Create batch from command line
|
||||
var batch = ResizeBatch.FromCommandLine(Console.In, commandLineArgs);
|
||||
|
||||
// Create main window (not yet visible – HWND is available for the file picker)
|
||||
var mainWindow = new MainWindow(new MainViewModel(batch, Settings.Default));
|
||||
_window = mainWindow;
|
||||
|
||||
mainWindow.DispatcherQueue.TryEnqueue(async () =>
|
||||
{
|
||||
if (batch.Files.Count == 0)
|
||||
{
|
||||
// Show file picker before the window is visible
|
||||
var files = await mainWindow.OpenPictureFilesAsync();
|
||||
if (!files.Any())
|
||||
{
|
||||
Environment.Exit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
batch.Files.Add(file);
|
||||
}
|
||||
}
|
||||
|
||||
// Load ViewModel (sets page content).
|
||||
// MainWindow will Activate itself after layout is settled to avoid flash.
|
||||
await mainWindow.LoadViewModelAsync();
|
||||
});
|
||||
}
|
||||
|
||||
private void InitializeAiAvailability()
|
||||
{
|
||||
// AI Super Resolution is currently disabled
|
||||
AiAvailabilityState = AiAvailabilityState.NotSupported;
|
||||
ResizeBatch.SetAiSuperResolutionService(NoOpAiSuperResolutionService.Instance);
|
||||
|
||||
// If AI is enabled in the future, uncomment this section:
|
||||
/*
|
||||
// AI Super Resolution is not supported on Windows 10
|
||||
if (OSVersionHelper.IsWindows10())
|
||||
{
|
||||
AiAvailabilityState = AiAvailabilityState.NotSupported;
|
||||
ResizeBatch.SetAiSuperResolutionService(NoOpAiSuperResolutionService.Instance);
|
||||
Logger.LogInfo("AI Super Resolution not supported on Windows 10");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Load AI availability from cache
|
||||
var cachedState = AiAvailabilityCacheService.LoadCache();
|
||||
|
||||
if (cachedState.HasValue)
|
||||
{
|
||||
AiAvailabilityState = cachedState.Value;
|
||||
Logger.LogInfo($"AI state loaded from cache: {AiAvailabilityState}");
|
||||
}
|
||||
else
|
||||
{
|
||||
AiAvailabilityState = AiAvailabilityState.NotSupported;
|
||||
Logger.LogInfo("No AI cache found, defaulting to NotSupported");
|
||||
}
|
||||
|
||||
// If AI is potentially available, start background initialization
|
||||
if (AiAvailabilityState == AiAvailabilityState.Ready)
|
||||
{
|
||||
_ = InitializeAiServiceAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
ResizeBatch.SetAiSuperResolutionService(NoOpAiSuperResolutionService.Instance);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// AI detection mode: perform detection, write to cache, and exit.
|
||||
/// </summary>
|
||||
private void RunAiDetectionMode()
|
||||
{
|
||||
try
|
||||
{
|
||||
Logger.LogInfo("Running AI detection mode...");
|
||||
|
||||
// AI is currently disabled
|
||||
AiAvailabilityCacheService.SaveCache(AiAvailabilityState.NotSupported);
|
||||
Logger.LogInfo("AI detection complete: NotSupported (feature disabled)");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"AI detection failed: {ex.Message}");
|
||||
AiAvailabilityCacheService.SaveCache(AiAvailabilityState.NotSupported);
|
||||
}
|
||||
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize AI Super Resolution service asynchronously in background.
|
||||
/// </summary>
|
||||
private static async Task InitializeAiServiceAsync()
|
||||
{
|
||||
AiAvailabilityState finalState;
|
||||
|
||||
try
|
||||
{
|
||||
var aiService = await WinAiSuperResolutionService.CreateAsync();
|
||||
|
||||
if (aiService != null)
|
||||
{
|
||||
ResizeBatch.SetAiSuperResolutionService(aiService);
|
||||
Logger.LogInfo("AI Super Resolution service initialized successfully.");
|
||||
finalState = AiAvailabilityState.Ready;
|
||||
}
|
||||
else
|
||||
{
|
||||
ResizeBatch.SetAiSuperResolutionService(NoOpAiSuperResolutionService.Instance);
|
||||
Logger.LogWarning("AI Super Resolution service initialization failed. Using default service.");
|
||||
finalState = AiAvailabilityState.NotSupported;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ResizeBatch.SetAiSuperResolutionService(NoOpAiSuperResolutionService.Instance);
|
||||
Logger.LogError($"Exception during AI service initialization: {ex.Message}");
|
||||
finalState = AiAvailabilityState.NotSupported;
|
||||
}
|
||||
|
||||
AiAvailabilityState = finalState;
|
||||
AiInitializationCompleted?.Invoke(null, finalState);
|
||||
}
|
||||
|
||||
private void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e)
|
||||
{
|
||||
Logger.LogError("Unhandled exception", e.Exception);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ResizeBatch.DisposeAiSuperResolutionService();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
33
src/modules/imageresizer/ui/ImageResizerXAML/MainWindow.xaml
Normal file
33
src/modules/imageresizer/ui/ImageResizerXAML/MainWindow.xaml
Normal file
@@ -0,0 +1,33 @@
|
||||
<!-- Copyright (c) Microsoft Corporation. All rights reserved. -->
|
||||
<!-- Licensed under the MIT License. -->
|
||||
<winuiex:WindowEx
|
||||
x:Class="ImageResizer.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:winuiex="using:WinUIEx"
|
||||
MinHeight="1"
|
||||
IsMaximizable="False"
|
||||
IsMinimizable="False"
|
||||
IsResizable="False"
|
||||
mc:Ignorable="d">
|
||||
<Window.SystemBackdrop>
|
||||
<MicaBackdrop />
|
||||
</Window.SystemBackdrop>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TitleBar
|
||||
x:Name="titleBar"
|
||||
x:Uid="TitleBarTitle"
|
||||
IsTabStop="False">
|
||||
<TitleBar.IconSource>
|
||||
<ImageIconSource ImageSource="/Assets/ImageResizer/ImageResizer.ico" />
|
||||
</TitleBar.IconSource>
|
||||
</TitleBar>
|
||||
<ContentPresenter x:Name="contentPresenter" Grid.Row="1" />
|
||||
</Grid>
|
||||
</winuiex:WindowEx>
|
||||
294
src/modules/imageresizer/ui/ImageResizerXAML/MainWindow.xaml.cs
Normal file
294
src/modules/imageresizer/ui/ImageResizerXAML/MainWindow.xaml.cs
Normal file
@@ -0,0 +1,294 @@
|
||||
// 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.
|
||||
|
||||
// Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using ImageResizer.ViewModels;
|
||||
using ImageResizer.Views;
|
||||
using ManagedCommon;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Windows.Graphics;
|
||||
using Windows.Storage.Pickers;
|
||||
using WinUIEx;
|
||||
|
||||
namespace ImageResizer
|
||||
{
|
||||
public sealed partial class MainWindow : WindowEx, IMainView
|
||||
{
|
||||
private const int MinWindowWidth = 460;
|
||||
private const int InitialWindowHeight = 1;
|
||||
private const double MaxWindowHeightScreenFraction = 0.85;
|
||||
|
||||
private bool _isFirstShow = true;
|
||||
|
||||
public MainViewModel ViewModel { get; }
|
||||
|
||||
private PropertyChangedEventHandler _selectedSizeChangedHandler;
|
||||
private InputViewModel _currentInputViewModel;
|
||||
|
||||
public MainWindow(MainViewModel viewModel)
|
||||
{
|
||||
ViewModel = viewModel;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
ExtendsContentIntoTitleBar = true;
|
||||
SetTitleBar(titleBar);
|
||||
this.SetIcon("Assets/ImageResizer/ImageResizer.ico");
|
||||
|
||||
WindowHelpers.ForceTopBorder1PixelInsetOnWindows10(this.GetWindowHandle());
|
||||
|
||||
// Keep the window hidden until content is loaded and measured.
|
||||
// A tiny provisional height avoids stretching the page during the first layout pass.
|
||||
this.SetWindowSize(MinWindowWidth, InitialWindowHeight);
|
||||
AppWindow.Hide();
|
||||
|
||||
// Listen to ViewModel property changes
|
||||
ViewModel.PropertyChanged += ViewModel_PropertyChanged;
|
||||
}
|
||||
|
||||
public async Task LoadViewModelAsync()
|
||||
{
|
||||
await ViewModel.LoadAsync(this);
|
||||
}
|
||||
|
||||
private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(ViewModel.CurrentPage))
|
||||
{
|
||||
UpdateCurrentPage();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCurrentPage()
|
||||
{
|
||||
var page = ViewModel.CurrentPage;
|
||||
if (page == null)
|
||||
{
|
||||
contentPresenter.Content = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (page is InputViewModel inputVM)
|
||||
{
|
||||
var inputPage = new InputPage { ViewModel = inputVM };
|
||||
contentPresenter.Content = inputPage;
|
||||
|
||||
AdjustWindowForInputPage(inputVM, inputPage);
|
||||
}
|
||||
else if (page is ProgressViewModel progressVM)
|
||||
{
|
||||
var progressPage = new ProgressPage { ViewModel = progressVM };
|
||||
contentPresenter.Content = progressPage;
|
||||
|
||||
SizeAndShowOnLoaded(progressPage);
|
||||
}
|
||||
else if (page is ResultsViewModel resultsVM)
|
||||
{
|
||||
var resultsPage = new ResultsPage { ViewModel = resultsVM };
|
||||
contentPresenter.Content = resultsPage;
|
||||
|
||||
SizeAndShowOnLoaded(resultsPage);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// After the element completes layout, size the window to fit and show it.
|
||||
/// </summary>
|
||||
private void SizeAndShowOnLoaded(FrameworkElement element)
|
||||
{
|
||||
void OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
element.Loaded -= OnLoaded;
|
||||
SizeToContent();
|
||||
ShowWindow();
|
||||
}
|
||||
|
||||
element.Loaded += OnLoaded;
|
||||
}
|
||||
|
||||
private void AdjustWindowForInputPage(InputViewModel inputVM, InputPage inputPage)
|
||||
{
|
||||
// Unsubscribe previous handler to prevent memory leak
|
||||
if (_selectedSizeChangedHandler != null && _currentInputViewModel?.Settings != null)
|
||||
{
|
||||
_currentInputViewModel.Settings.PropertyChanged -= _selectedSizeChangedHandler;
|
||||
}
|
||||
|
||||
_currentInputViewModel = inputVM;
|
||||
|
||||
// Create and store handler reference for future cleanup
|
||||
_selectedSizeChangedHandler = (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(inputVM.Settings.SelectedSizeIndex))
|
||||
{
|
||||
// Content visibility changes after the selected size option changes;
|
||||
// listen for the next LayoutUpdated to re-measure once layout settles.
|
||||
SizeToContentAfterLayout(inputPage);
|
||||
}
|
||||
};
|
||||
|
||||
inputVM.Settings.PropertyChanged += _selectedSizeChangedHandler;
|
||||
|
||||
SizeAndShowOnLoaded(inputPage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activate and center the window on first show; subsequent calls are no-ops.
|
||||
/// </summary>
|
||||
private void ShowWindow()
|
||||
{
|
||||
if (_isFirstShow)
|
||||
{
|
||||
_isFirstShow = false;
|
||||
this.CenterOnScreen();
|
||||
this.Show();
|
||||
Activate();
|
||||
|
||||
// Compact the visible window after the first shown layout pass.
|
||||
// This trims any slack that remains after hidden-state sizing.
|
||||
DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
CompactWindowToRenderedContent();
|
||||
this.CenterOnScreen();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sizes the window after the next layout pass completes.
|
||||
/// Used when content changes on an already-loaded element (e.g., visibility toggles).
|
||||
/// LayoutUpdated fires once per layout pass, so we unsubscribe immediately.
|
||||
/// </summary>
|
||||
private void SizeToContentAfterLayout(FrameworkElement element)
|
||||
{
|
||||
void OnLayoutUpdated(object sender, object e)
|
||||
{
|
||||
element.LayoutUpdated -= OnLayoutUpdated;
|
||||
SizeToContent();
|
||||
|
||||
DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, CompactWindowToRenderedContent);
|
||||
}
|
||||
|
||||
element.LayoutUpdated += OnLayoutUpdated;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WinUI3 has no built-in SizeToContent (unlike WPF).
|
||||
/// Measure the title bar and current page content at the current client width with
|
||||
/// unconstrained height, then resize the client area to match.
|
||||
///
|
||||
/// Measuring the Page itself can over-report height because the Page may already be
|
||||
/// stretched to the window's provisional size from the first layout pass.
|
||||
/// </summary>
|
||||
private void SizeToContent()
|
||||
{
|
||||
var pageContentRoot = GetCurrentPageContentRoot();
|
||||
if (pageContentRoot == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var scale = this.GetDpiForWindow() / 96.0;
|
||||
var clientWidth = AppWindow.ClientSize.Width;
|
||||
var availableWidth = clientWidth / scale;
|
||||
|
||||
titleBar.Measure(new Windows.Foundation.Size(availableWidth, double.PositiveInfinity));
|
||||
pageContentRoot.Measure(new Windows.Foundation.Size(availableWidth, double.PositiveInfinity));
|
||||
|
||||
var desiredHeight = titleBar.DesiredSize.Height + pageContentRoot.DesiredSize.Height;
|
||||
|
||||
if (desiredHeight <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyWindowSizeForClientContent(desiredHeight);
|
||||
}
|
||||
|
||||
private FrameworkElement GetCurrentPageContentRoot()
|
||||
{
|
||||
if (contentPresenter.Content is Page page)
|
||||
{
|
||||
return page.Content as FrameworkElement ?? page;
|
||||
}
|
||||
|
||||
return contentPresenter.Content as FrameworkElement;
|
||||
}
|
||||
|
||||
private void CompactWindowToRenderedContent()
|
||||
{
|
||||
var pageContentRoot = GetCurrentPageContentRoot();
|
||||
var windowContentRoot = this.Content as FrameworkElement;
|
||||
if (pageContentRoot == null || windowContentRoot == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var totalRenderedHeight = windowContentRoot.ActualHeight;
|
||||
var occupiedHeight = titleBar.ActualHeight + pageContentRoot.ActualHeight;
|
||||
var slackHeight = totalRenderedHeight - occupiedHeight;
|
||||
|
||||
if (slackHeight <= 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var reducedHeight = totalRenderedHeight - slackHeight;
|
||||
|
||||
if (reducedHeight <= 0 || reducedHeight >= totalRenderedHeight)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyWindowSizeForClientContent(reducedHeight);
|
||||
}
|
||||
|
||||
private void ApplyWindowSizeForClientContent(double desiredClientHeight)
|
||||
{
|
||||
var scale = this.GetDpiForWindow() / 96.0;
|
||||
var frameHeight = Math.Max(0, AppWindow.Size.Height - AppWindow.ClientSize.Height) / scale;
|
||||
var outerHeight = desiredClientHeight + frameHeight;
|
||||
|
||||
var displayArea = Microsoft.UI.Windowing.DisplayArea.GetFromWindowId(AppWindow.Id, Microsoft.UI.Windowing.DisplayAreaFallback.Nearest);
|
||||
var maxScreenHeight = displayArea.WorkArea.Height / scale * MaxWindowHeightScreenFraction;
|
||||
outerHeight = Math.Min(outerHeight, maxScreenHeight);
|
||||
|
||||
this.SetWindowSize(MinWindowWidth, outerHeight);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<string>> OpenPictureFilesAsync()
|
||||
{
|
||||
var picker = this.CreateOpenFilePicker();
|
||||
picker.ViewMode = PickerViewMode.Thumbnail;
|
||||
picker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
|
||||
string[] imageExtensions = [".bmp", ".dib", ".exif", ".gif", ".jfif", ".jpe",
|
||||
".jpeg", ".jpg", ".jxr", ".png", ".rle", ".tif", ".tiff", ".wdp"];
|
||||
|
||||
foreach (var ext in imageExtensions)
|
||||
{
|
||||
picker.FileTypeFilter.Add(ext);
|
||||
}
|
||||
|
||||
var files = await picker.PickMultipleFilesAsync();
|
||||
if (files != null && files.Count > 0)
|
||||
{
|
||||
return files.Select(f => f.Path);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
void IMainView.Close()
|
||||
{
|
||||
DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Normal, Close);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// 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.
|
||||
|
||||
// Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ImageResizer.Views
|
||||
{
|
||||
public interface IMainView
|
||||
{
|
||||
Task<IEnumerable<string>> OpenPictureFilesAsync();
|
||||
|
||||
void Close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
<!-- Copyright (c) Microsoft Corporation. All rights reserved. -->
|
||||
<!-- Licensed under the MIT License. -->
|
||||
<Page
|
||||
x:Class="ImageResizer.Views.InputPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:ImageResizer.Views"
|
||||
xmlns:m="using:ImageResizer.Models"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Top"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Page.Resources>
|
||||
<!-- Template for normal ResizeSize presets (Small, Medium, Large, Phone) -->
|
||||
<DataTemplate x:Key="ResizeSizeTemplate" x:DataType="m:ResizeSize">
|
||||
<Grid VerticalAlignment="Center" AutomationProperties.Name="{x:Bind Name}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Style="{StaticResource BodyStrongTextBlockStyle}" Text="{x:Bind Name}" />
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal">
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind Fit, Converter={StaticResource EnumValueConverter}, ConverterParameter=ThirdPersonSingular}" />
|
||||
<TextBlock
|
||||
Margin="4,0,0,0"
|
||||
Style="{StaticResource BodyStrongTextBlockStyle}"
|
||||
Text="{x:Bind Width, Converter={StaticResource AutoDoubleConverter}, ConverterParameter=Auto}" />
|
||||
<TextBlock
|
||||
Margin="4,0,0,0"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="×"
|
||||
Visibility="{x:Bind ShowHeight, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<TextBlock
|
||||
Margin="4,0,0,0"
|
||||
Style="{StaticResource BodyStrongTextBlockStyle}"
|
||||
Text="{x:Bind Height, Converter={StaticResource AutoDoubleConverter}, ConverterParameter=Auto}"
|
||||
Visibility="{x:Bind ShowHeight, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<TextBlock Margin="4,0,0,0" Text="{x:Bind Unit, Converter={StaticResource EnumValueConverter}, ConverterParameter=ToLower}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
<!-- Template for CustomSize - shows only name -->
|
||||
<DataTemplate x:Key="CustomSizeTemplate" x:DataType="m:CustomSize">
|
||||
<Grid VerticalAlignment="Center" AutomationProperties.Name="{x:Bind Name}">
|
||||
<TextBlock Style="{StaticResource BodyStrongTextBlockStyle}" Text="{x:Bind Name}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
<!-- Template for AiSize - shows name and description -->
|
||||
<DataTemplate x:Key="AiSizeTemplate" x:DataType="m:AiSize">
|
||||
<StackPanel
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.Name="{x:Bind Name}"
|
||||
Orientation="Vertical">
|
||||
<TextBlock Style="{StaticResource BodyStrongTextBlockStyle}" Text="{x:Bind Name}" />
|
||||
<TextBlock x:Uid="Input_AiSuperResolutionDescription" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
|
||||
<!-- DataTemplateSelector to choose the right template based on item type -->
|
||||
<local:SizeDataTemplateSelector
|
||||
x:Key="SizeTemplateSelector"
|
||||
AiSizeTemplate="{StaticResource AiSizeTemplate}"
|
||||
CustomSizeTemplate="{StaticResource CustomSizeTemplate}"
|
||||
ResizeSizeTemplate="{StaticResource ResizeSizeTemplate}" />
|
||||
</Page.Resources>
|
||||
|
||||
<Grid VerticalAlignment="Top">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Grid.Row="0" Margin="16">
|
||||
<ComboBox
|
||||
x:Name="SizeComboBox"
|
||||
MinHeight="64"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
VerticalContentAlignment="Stretch"
|
||||
ItemTemplateSelector="{StaticResource SizeTemplateSelector}"
|
||||
ItemsSource="{x:Bind ViewModel.Settings.AllSizes, Mode=OneWay}"
|
||||
SelectedIndex="{x:Bind ViewModel.Settings.SelectedSizeIndex, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Content area with gray background (AI/Custom panels + checkboxes) -->
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Border
|
||||
Grid.RowSpan="2"
|
||||
Background="{ThemeResource LayerFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="0,1,0,1" />
|
||||
|
||||
<!-- AI Configuration Panel -->
|
||||
<Grid
|
||||
Grid.Row="0"
|
||||
Margin="16,16,16,8"
|
||||
Visibility="{x:Bind ViewModel.ShowAiConfigurationSection, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<!-- AI Model Download Prompt -->
|
||||
<StackPanel Visibility="{x:Bind ViewModel.ShowModelDownloadPrompt, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<InfoBar
|
||||
IsClosable="False"
|
||||
IsOpen="True"
|
||||
Message="{x:Bind ViewModel.ModelStatusMessage, Mode=OneWay}"
|
||||
Severity="Informational" />
|
||||
|
||||
<Button
|
||||
Margin="0,8,0,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
Command="{x:Bind ViewModel.DownloadModelCommand}"
|
||||
Style="{StaticResource AccentButtonStyle}"
|
||||
Visibility="{x:Bind ViewModel.IsDownloadingModel, Mode=OneWay, Converter={StaticResource InvertedBoolToVisibilityConverter}}">
|
||||
<TextBlock x:Uid="Input_AiModelDownloadButton" />
|
||||
</Button>
|
||||
|
||||
<StackPanel Margin="0,8,0,0" Visibility="{x:Bind ViewModel.IsDownloadingModel, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<ProgressBar IsIndeterminate="True" />
|
||||
<TextBlock
|
||||
Margin="0,8,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
Text="{x:Bind ViewModel.ModelStatusMessage, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<!-- AI Scale Controls -->
|
||||
<StackPanel Visibility="{x:Bind ViewModel.ShowAiControls, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<Grid>
|
||||
<TextBlock x:Uid="Input_AiCurrentLabel" />
|
||||
<TextBlock HorizontalAlignment="Right" Text="{x:Bind ViewModel.AiScaleDisplay, Mode=OneWay}" />
|
||||
</Grid>
|
||||
|
||||
<Slider
|
||||
Margin="0,8,0,0"
|
||||
Maximum="8"
|
||||
Minimum="1"
|
||||
TickFrequency="1"
|
||||
TickPlacement="BottomRight"
|
||||
Value="{x:Bind ViewModel.AiSuperResolutionScale, Mode=TwoWay}" />
|
||||
|
||||
<StackPanel Margin="0,16,0,0" Visibility="{x:Bind ViewModel.ShowAiSizeDescriptions, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<Grid>
|
||||
<TextBlock x:Uid="Input_AiCurrentLabel" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Right"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="{x:Bind ViewModel.CurrentResolutionDescription, Mode=OneWay}" />
|
||||
</Grid>
|
||||
<Grid Margin="0,8,0,0">
|
||||
<TextBlock x:Uid="Input_AiNewLabel" />
|
||||
<TextBlock HorizontalAlignment="Right" Text="{x:Bind ViewModel.NewResolutionDescription, Mode=OneWay}" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<!-- Custom input matrix -->
|
||||
<Grid
|
||||
Grid.Row="0"
|
||||
Margin="16,16,16,8"
|
||||
ColumnSpacing="8"
|
||||
RowSpacing="8"
|
||||
Visibility="{x:Bind ViewModel.IsCustomSizeSelected, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<FontIcon
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
AutomationProperties.Name="Width"
|
||||
FontSize="16"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Glyph="" />
|
||||
<NumberBox
|
||||
x:Name="WidthNumberBox"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
KeyDown="NumberBox_KeyDown"
|
||||
Minimum="0"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
Value="{x:Bind ViewModel.Settings.CustomSize.Width, Mode=TwoWay}" />
|
||||
|
||||
<FontIcon
|
||||
Grid.Column="2"
|
||||
Margin="8,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
AutomationProperties.Name="Height"
|
||||
FontSize="16"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Glyph=""
|
||||
RenderTransformOrigin="0.5,0.5"
|
||||
Visibility="{x:Bind ViewModel.Settings.CustomSize.ShowHeight, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<FontIcon.RenderTransform>
|
||||
<RotateTransform Angle="90" />
|
||||
</FontIcon.RenderTransform>
|
||||
</FontIcon>
|
||||
<NumberBox
|
||||
x:Name="HeightNumberBox"
|
||||
Grid.Column="3"
|
||||
HorizontalAlignment="Stretch"
|
||||
KeyDown="NumberBox_KeyDown"
|
||||
Minimum="0"
|
||||
SpinButtonPlacementMode="Inline"
|
||||
Visibility="{x:Bind ViewModel.Settings.CustomSize.ShowHeight, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}"
|
||||
Value="{x:Bind ViewModel.Settings.CustomSize.Height, Mode=TwoWay}" />
|
||||
<FontIcon
|
||||
Grid.Row="1"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontSize="16"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Glyph="" />
|
||||
<ComboBox
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemsSource="{x:Bind ViewModel.ResizeFitValues, Mode=OneWay}"
|
||||
SelectedIndex="{x:Bind ViewModel.Settings.CustomSize.Fit, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="m:ResizeFit">
|
||||
<TextBlock Padding="2,0" Text="{x:Bind Converter={StaticResource EnumValueConverter}}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<FontIcon
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Margin="8,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontSize="16"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Glyph="" />
|
||||
<ComboBox
|
||||
Grid.Row="1"
|
||||
Grid.Column="3"
|
||||
HorizontalAlignment="Stretch"
|
||||
ItemsSource="{x:Bind ViewModel.ResizeUnitValues, Mode=OneWay}"
|
||||
SelectedIndex="{x:Bind ViewModel.Settings.CustomSize.Unit, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="m:ResizeUnit">
|
||||
<TextBlock Padding="2,0" Text="{x:Bind Converter={StaticResource EnumValueConverter}}" />
|
||||
</DataTemplate>
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
|
||||
<!-- CheckBoxes -->
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Margin="16,8,16,16"
|
||||
Orientation="Vertical">
|
||||
<CheckBox IsChecked="{x:Bind ViewModel.Settings.ShrinkOnly, Mode=TwoWay}">
|
||||
<TextBlock x:Uid="Input_ShrinkOnly" TextWrapping="Wrap" />
|
||||
</CheckBox>
|
||||
|
||||
<CheckBox IsChecked="{x:Bind ViewModel.Settings.IgnoreOrientation, Mode=TwoWay}">
|
||||
<TextBlock x:Uid="Input_IgnoreOrientation" TextWrapping="Wrap" />
|
||||
</CheckBox>
|
||||
|
||||
<CheckBox IsChecked="{x:Bind ViewModel.Settings.Replace, Mode=TwoWay}">
|
||||
<TextBlock x:Uid="Input_Replace" TextWrapping="Wrap" />
|
||||
</CheckBox>
|
||||
|
||||
<CheckBox IsChecked="{x:Bind ViewModel.Settings.RemoveMetadata, Mode=TwoWay}">
|
||||
<TextBlock x:Uid="Input_RemoveMetadata" TextWrapping="Wrap" />
|
||||
</CheckBox>
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
|
||||
<!-- GIF warning (outside gray area) -->
|
||||
<InfoBar
|
||||
x:Uid="Input_GifWarning"
|
||||
Grid.Row="2"
|
||||
Margin="16,8,16,0"
|
||||
IsClosable="False"
|
||||
IsOpen="True"
|
||||
Severity="Warning"
|
||||
Visibility="{x:Bind ViewModel.HasGifFiles, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
|
||||
<!-- Buttons (outside gray area) -->
|
||||
<Grid Grid.Row="3" Margin="8,16,16,16">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button
|
||||
x:Uid="Open_settings_button"
|
||||
Padding="8"
|
||||
Command="{x:Bind ViewModel.OpenSettingsCommand}"
|
||||
Style="{StaticResource SubtleButtonStyle}">
|
||||
<FontIcon FontSize="16" Glyph="" />
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="Open_settings" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
Grid.Column="1"
|
||||
MinWidth="{StaticResource ButtonMinWidth}"
|
||||
Command="{x:Bind ViewModel.ResizeCommand}"
|
||||
Style="{StaticResource AccentButtonStyle}">
|
||||
<Button.KeyboardAccelerators>
|
||||
<KeyboardAccelerator Key="Enter" Invoked="ResizeAccelerator_Invoked" />
|
||||
</Button.KeyboardAccelerators>
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<FontIcon FontSize="14" Glyph="" />
|
||||
<TextBlock x:Uid="Input_Resize" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
MinWidth="{StaticResource ButtonMinWidth}"
|
||||
Margin="8,0,0,0"
|
||||
Command="{x:Bind ViewModel.CancelCommand}">
|
||||
<Button.KeyboardAccelerators>
|
||||
<KeyboardAccelerator Key="Escape" />
|
||||
</Button.KeyboardAccelerators>
|
||||
<TextBlock x:Uid="Cancel" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -0,0 +1,52 @@
|
||||
// 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.
|
||||
|
||||
// Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
using ImageResizer.ViewModels;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
|
||||
using static ImageResizer.ViewModels.InputViewModel;
|
||||
|
||||
namespace ImageResizer.Views
|
||||
{
|
||||
public sealed partial class InputPage : Page
|
||||
{
|
||||
public InputViewModel ViewModel { get; set; }
|
||||
|
||||
public InputPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void ResizeAccelerator_Invoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
|
||||
{
|
||||
if (FocusManager.GetFocusedElement(XamlRoot) is NumberBox)
|
||||
{
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void NumberBox_KeyDown(object sender, KeyRoutedEventArgs e)
|
||||
{
|
||||
if (e.Key == Windows.System.VirtualKey.Enter
|
||||
&& sender is NumberBox numberBox
|
||||
&& ViewModel is not null
|
||||
&& !double.IsNaN(numberBox.Value))
|
||||
{
|
||||
KeyPressParams keyParams = numberBox.Name switch
|
||||
{
|
||||
"WidthNumberBox" => new KeyPressParams { Value = numberBox.Value, Dimension = Dimension.Width },
|
||||
"HeightNumberBox" => new KeyPressParams { Value = numberBox.Value, Dimension = Dimension.Height },
|
||||
_ => null,
|
||||
};
|
||||
|
||||
if (keyParams is not null)
|
||||
{
|
||||
ViewModel.EnterKeyPressedCommand.Execute(keyParams);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<!-- Copyright (c) Microsoft Corporation. All rights reserved. -->
|
||||
<!-- Licensed under the MIT License. -->
|
||||
<Page
|
||||
x:Class="ImageResizer.Views.ProgressPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Loaded="Page_Loaded"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
x:Uid="Progress_MainInstruction"
|
||||
Margin="{StaticResource PageSectionMargin}"
|
||||
Style="{StaticResource SubtitleTextBlockStyle}" />
|
||||
<TextBlock
|
||||
Margin="{StaticResource PageSectionMargin}"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="{x:Bind ViewModel.TimeRemainingDisplay, Mode=OneWay}" />
|
||||
<ProgressBar
|
||||
Height="16"
|
||||
Margin="{StaticResource PageSectionMargin}"
|
||||
Maximum="1"
|
||||
Value="{x:Bind ViewModel.Progress, Mode=OneWay}" />
|
||||
<Border
|
||||
Margin="0,12,0,0"
|
||||
Padding="12"
|
||||
Background="{ThemeResource LayerFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
|
||||
BorderThickness="0,1,0,0">
|
||||
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
|
||||
<Button MinWidth="{StaticResource ButtonMinWidth}" Command="{x:Bind ViewModel.StopCommand}">
|
||||
<Button.KeyboardAccelerators>
|
||||
<KeyboardAccelerator Key="Escape" />
|
||||
</Button.KeyboardAccelerators>
|
||||
<TextBlock x:Uid="Progress_Stop" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Page>
|
||||
@@ -0,0 +1,26 @@
|
||||
// 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.
|
||||
|
||||
// Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
using ImageResizer.ViewModels;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace ImageResizer.Views
|
||||
{
|
||||
public sealed partial class ProgressPage : Page
|
||||
{
|
||||
public ProgressViewModel ViewModel { get; set; }
|
||||
|
||||
public ProgressPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void Page_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ViewModel?.StartCommand.Execute(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<!-- Copyright (c) Microsoft Corporation. All rights reserved. -->
|
||||
<!-- Licensed under the MIT License. -->
|
||||
<Page
|
||||
x:Class="ImageResizer.Views.ResultsPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:m="using:ImageResizer.Models"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock
|
||||
x:Uid="Results_MainInstruction"
|
||||
Margin="{StaticResource PageSectionMargin}"
|
||||
Style="{StaticResource SubtitleTextBlockStyle}" />
|
||||
|
||||
<ScrollViewer
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<ItemsControl Margin="12,4,12,0" ItemsSource="{x:Bind ViewModel.Errors, Mode=OneWay}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="m:ResizeError">
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
Margin="0,8,0,0"
|
||||
FontWeight="Bold"
|
||||
Text="{x:Bind File}" />
|
||||
<TextBlock Text="{x:Bind Error}" TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ScrollViewer>
|
||||
|
||||
<Border
|
||||
Grid.Row="2"
|
||||
Margin="0,12,0,0"
|
||||
Padding="12"
|
||||
Background="{ThemeResource LayerFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
|
||||
BorderThickness="0,1,0,0">
|
||||
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
|
||||
<Button MinWidth="{StaticResource ButtonMinWidth}" Command="{x:Bind ViewModel.CloseCommand}">
|
||||
<Button.KeyboardAccelerators>
|
||||
<KeyboardAccelerator Key="Escape" />
|
||||
</Button.KeyboardAccelerators>
|
||||
<TextBlock x:Uid="Results_Close" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -0,0 +1,20 @@
|
||||
// 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.
|
||||
|
||||
// Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
using ImageResizer.ViewModels;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace ImageResizer.Views
|
||||
{
|
||||
public sealed partial class ResultsPage : Page
|
||||
{
|
||||
public ResultsViewModel ViewModel { get; set; }
|
||||
|
||||
public ResultsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using ImageResizer.Models;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace ImageResizer.Views
|
||||
{
|
||||
public partial class SizeDataTemplateSelector : DataTemplateSelector
|
||||
{
|
||||
public DataTemplate ResizeSizeTemplate { get; set; }
|
||||
public DataTemplate CustomSizeTemplate { get; set; }
|
||||
public DataTemplate AiSizeTemplate { get; set; }
|
||||
|
||||
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
|
||||
{
|
||||
if (item is AiSize)
|
||||
{
|
||||
return AiSizeTemplate;
|
||||
}
|
||||
|
||||
if (item is CustomSize)
|
||||
{
|
||||
return CustomSizeTemplate;
|
||||
}
|
||||
|
||||
if (item is ResizeSize)
|
||||
{
|
||||
return ResizeSizeTemplate;
|
||||
}
|
||||
|
||||
return base.SelectTemplateCore(item, container);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,31 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using ImageResizer.Properties;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using ImageResizer.Helpers;
|
||||
|
||||
namespace ImageResizer.Models
|
||||
{
|
||||
public class AiSize : ResizeSize
|
||||
public partial class AiSize : ResizeSize
|
||||
{
|
||||
private static readonly CompositeFormat ScaleFormat = CompositeFormat.Parse(Resources.Input_AiScaleFormat);
|
||||
private static CompositeFormat _scaleFormat;
|
||||
|
||||
private static CompositeFormat ScaleFormat =>
|
||||
_scaleFormat ??= CompositeFormat.Parse(ResourceLoaderInstance.GetString("Input_AiScaleFormat"));
|
||||
|
||||
[ObservableProperty]
|
||||
[JsonPropertyName("scale")]
|
||||
private int _scale = 2;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the formatted scale display string (e.g., "2×").
|
||||
/// Gets the formatted scale display string (e.g., "2x").
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public string ScaleDisplay => string.Format(CultureInfo.CurrentCulture, ScaleFormat, _scale);
|
||||
|
||||
[JsonPropertyName("scale")]
|
||||
public int Scale
|
||||
{
|
||||
get => _scale;
|
||||
set => Set(ref _scale, value);
|
||||
}
|
||||
public string ScaleDisplay => string.Format(CultureInfo.CurrentCulture, ScaleFormat, Scale);
|
||||
|
||||
[JsonConstructor]
|
||||
public AiSize(int scale)
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Collections.ObjectModel;
|
||||
using System.CommandLine.Parsing;
|
||||
using System.Globalization;
|
||||
using ImageResizer.Cli.Commands;
|
||||
using ImageResizer.Helpers;
|
||||
|
||||
#pragma warning disable SA1649 // File name should match first type name
|
||||
#pragma warning disable SA1402 // File may only contain a single type
|
||||
@@ -19,117 +20,51 @@ namespace ImageResizer.Models
|
||||
/// </summary>
|
||||
public class CliOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to show help information.
|
||||
/// </summary>
|
||||
public bool ShowHelp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to show current configuration.
|
||||
/// </summary>
|
||||
public bool ShowConfig { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the destination directory for resized images.
|
||||
/// </summary>
|
||||
public string DestinationDirectory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the width of the resized image.
|
||||
/// </summary>
|
||||
public double? Width { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the height of the resized image.
|
||||
/// </summary>
|
||||
public double? Height { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the resize unit (Pixel, Percent, Inch, Centimeter).
|
||||
/// </summary>
|
||||
public ResizeUnit? Unit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the resize fit mode (Fill, Fit, Stretch).
|
||||
/// </summary>
|
||||
public ResizeFit? Fit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the index of the preset size to use.
|
||||
/// </summary>
|
||||
public int? SizeIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to only shrink images (not enlarge).
|
||||
/// </summary>
|
||||
public bool? ShrinkOnly { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to replace the original file.
|
||||
/// </summary>
|
||||
public bool? Replace { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to ignore orientation when resizing.
|
||||
/// </summary>
|
||||
public bool? IgnoreOrientation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to remove metadata from the resized image.
|
||||
/// </summary>
|
||||
public bool? RemoveMetadata { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the JPEG quality level (1-100).
|
||||
/// </summary>
|
||||
public int? JpegQualityLevel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to keep the date modified.
|
||||
/// </summary>
|
||||
public bool? KeepDateModified { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the output filename format.
|
||||
/// </summary>
|
||||
public string FileName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to use line-based progress output for screen reader accessibility.
|
||||
/// </summary>
|
||||
public bool? ProgressLines { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of files to process.
|
||||
/// </summary>
|
||||
public ICollection<string> Files { get; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the pipe name for receiving file list.
|
||||
/// </summary>
|
||||
public string PipeName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets parse/validation errors produced by System.CommandLine.
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> ParseErrors { get; private set; } = Array.Empty<string>();
|
||||
public IReadOnlyList<string> ParseErrors { get; private set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Converts a boolean value to nullable bool (true -> true, false -> null).
|
||||
/// </summary>
|
||||
private static bool? ToBoolOrNull(bool value) => value ? true : null;
|
||||
|
||||
/// <summary>
|
||||
/// Parses command-line arguments into CliOptions using System.CommandLine.
|
||||
/// </summary>
|
||||
/// <param name="args">The command-line arguments.</param>
|
||||
/// <returns>A CliOptions instance with parsed values.</returns>
|
||||
public static CliOptions Parse(string[] args)
|
||||
{
|
||||
var options = new CliOptions();
|
||||
var cmd = new ImageResizerRootCommand();
|
||||
|
||||
// Parse using System.CommandLine
|
||||
var parseResult = new Parser(cmd).Parse(args);
|
||||
|
||||
if (parseResult.Errors.Count > 0)
|
||||
@@ -143,7 +78,6 @@ namespace ImageResizer.Models
|
||||
options.ParseErrors = new ReadOnlyCollection<string>(errors);
|
||||
}
|
||||
|
||||
// Extract values from parse result using strongly typed options
|
||||
options.ShowHelp = parseResult.GetValueForOption(cmd.HelpOption);
|
||||
options.ShowConfig = parseResult.GetValueForOption(cmd.ShowConfigOption);
|
||||
options.DestinationDirectory = parseResult.GetValueForOption(cmd.DestinationOption);
|
||||
@@ -153,7 +87,6 @@ namespace ImageResizer.Models
|
||||
options.Fit = parseResult.GetValueForOption(cmd.FitOption);
|
||||
options.SizeIndex = parseResult.GetValueForOption(cmd.SizeOption);
|
||||
|
||||
// Convert bool to nullable bool (true -> true, false -> null)
|
||||
options.ShrinkOnly = ToBoolOrNull(parseResult.GetValueForOption(cmd.ShrinkOnlyOption));
|
||||
options.Replace = ToBoolOrNull(parseResult.GetValueForOption(cmd.ReplaceOption));
|
||||
options.IgnoreOrientation = ToBoolOrNull(parseResult.GetValueForOption(cmd.IgnoreOrientationOption));
|
||||
@@ -165,14 +98,12 @@ namespace ImageResizer.Models
|
||||
|
||||
options.FileName = parseResult.GetValueForOption(cmd.FileNameOption);
|
||||
|
||||
// Get files from arguments
|
||||
var files = parseResult.GetValueForArgument(cmd.FilesArgument);
|
||||
if (files != null)
|
||||
{
|
||||
const string pipeNamePrefix = "\\\\.\\pipe\\";
|
||||
foreach (var file in files)
|
||||
{
|
||||
// Check for pipe name (must be at the start of the path)
|
||||
if (file.StartsWith(pipeNamePrefix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
options.PipeName = file.Substring(pipeNamePrefix.Length);
|
||||
@@ -187,62 +118,55 @@ namespace ImageResizer.Models
|
||||
return options;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prints current configuration to the console.
|
||||
/// </summary>
|
||||
/// <param name="settings">The settings to display.</param>
|
||||
public static void PrintConfig(ImageResizer.Properties.Settings settings)
|
||||
{
|
||||
var getString = ResourceLoaderInstance.GetString;
|
||||
Console.OutputEncoding = System.Text.Encoding.UTF8;
|
||||
Console.WriteLine(Properties.Resources.CLI_ConfigTitle);
|
||||
Console.WriteLine(getString("CLI_ConfigTitle"));
|
||||
Console.WriteLine();
|
||||
Console.WriteLine(Properties.Resources.CLI_ConfigGeneralSettings);
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, Properties.Resources.CLI_ConfigShrinkOnly, settings.ShrinkOnly));
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, Properties.Resources.CLI_ConfigReplaceOriginal, settings.Replace));
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, Properties.Resources.CLI_ConfigIgnoreOrientation, settings.IgnoreOrientation));
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, Properties.Resources.CLI_ConfigRemoveMetadata, settings.RemoveMetadata));
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, Properties.Resources.CLI_ConfigKeepDateModified, settings.KeepDateModified));
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, Properties.Resources.CLI_ConfigJpegQuality, settings.JpegQualityLevel));
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, Properties.Resources.CLI_ConfigPngInterlace, settings.PngInterlaceOption));
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, Properties.Resources.CLI_ConfigTiffCompress, settings.TiffCompressOption));
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, Properties.Resources.CLI_ConfigFilenameFormat, settings.FileName));
|
||||
Console.WriteLine(getString("CLI_ConfigGeneralSettings"));
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, getString("CLI_ConfigShrinkOnly"), settings.ShrinkOnly));
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, getString("CLI_ConfigReplaceOriginal"), settings.Replace));
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, getString("CLI_ConfigIgnoreOrientation"), settings.IgnoreOrientation));
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, getString("CLI_ConfigRemoveMetadata"), settings.RemoveMetadata));
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, getString("CLI_ConfigKeepDateModified"), settings.KeepDateModified));
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, getString("CLI_ConfigJpegQuality"), settings.JpegQualityLevel));
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, getString("CLI_ConfigPngInterlace"), settings.PngInterlaceOption));
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, getString("CLI_ConfigTiffCompress"), settings.TiffCompressOption));
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, getString("CLI_ConfigFilenameFormat"), settings.FileName));
|
||||
Console.WriteLine();
|
||||
Console.WriteLine(Properties.Resources.CLI_ConfigCustomSize);
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, Properties.Resources.CLI_ConfigWidth, settings.CustomSize.Width, settings.CustomSize.Unit));
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, Properties.Resources.CLI_ConfigHeight, settings.CustomSize.Height, settings.CustomSize.Unit));
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, Properties.Resources.CLI_ConfigFitMode, settings.CustomSize.Fit));
|
||||
Console.WriteLine(getString("CLI_ConfigCustomSize"));
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, getString("CLI_ConfigWidth"), settings.CustomSize.Width, settings.CustomSize.Unit));
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, getString("CLI_ConfigHeight"), settings.CustomSize.Height, settings.CustomSize.Unit));
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, getString("CLI_ConfigFitMode"), settings.CustomSize.Fit));
|
||||
Console.WriteLine();
|
||||
Console.WriteLine(Properties.Resources.CLI_ConfigPresetSizes);
|
||||
Console.WriteLine(getString("CLI_ConfigPresetSizes"));
|
||||
for (int i = 0; i < settings.Sizes.Count; i++)
|
||||
{
|
||||
var size = settings.Sizes[i];
|
||||
var selected = i == settings.SelectedSizeIndex ? "*" : " ";
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, Properties.Resources.CLI_ConfigPresetSizeFormat, i, selected, size.Name, size.Width, size.Height, size.Unit, size.Fit));
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, getString("CLI_ConfigPresetSizeFormat"), i, selected, size.Name, size.Width, size.Height, size.Unit, size.Fit));
|
||||
}
|
||||
|
||||
if (settings.SelectedSizeIndex >= settings.Sizes.Count)
|
||||
{
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, Properties.Resources.CLI_ConfigCustomSelected, settings.CustomSize.Width, settings.CustomSize.Height, settings.CustomSize.Unit, settings.CustomSize.Fit));
|
||||
Console.WriteLine(string.Format(CultureInfo.InvariantCulture, getString("CLI_ConfigCustomSelected"), settings.CustomSize.Width, settings.CustomSize.Height, settings.CustomSize.Unit, settings.CustomSize.Fit));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prints usage information to the console.
|
||||
/// </summary>
|
||||
public static void PrintUsage()
|
||||
{
|
||||
var getString = ResourceLoaderInstance.GetString;
|
||||
Console.OutputEncoding = System.Text.Encoding.UTF8;
|
||||
Console.WriteLine(Properties.Resources.CLI_UsageTitle);
|
||||
Console.WriteLine(getString("CLI_UsageTitle"));
|
||||
Console.WriteLine();
|
||||
|
||||
var cmd = new ImageResizerRootCommand();
|
||||
|
||||
// Print usage line
|
||||
Console.WriteLine(Properties.Resources.CLI_UsageLine);
|
||||
Console.WriteLine(getString("CLI_UsageLine"));
|
||||
Console.WriteLine();
|
||||
|
||||
// Print options from the command definition
|
||||
Console.WriteLine(Properties.Resources.CLI_UsageOptions);
|
||||
Console.WriteLine(getString("CLI_UsageOptions"));
|
||||
foreach (var option in cmd.Options)
|
||||
{
|
||||
var aliases = string.Join(", ", option.Aliases);
|
||||
@@ -251,11 +175,11 @@ namespace ImageResizer.Models
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine(Properties.Resources.CLI_UsageExamples);
|
||||
Console.WriteLine(Properties.Resources.CLI_UsageExampleHelp);
|
||||
Console.WriteLine(Properties.Resources.CLI_UsageExampleDimensions);
|
||||
Console.WriteLine(Properties.Resources.CLI_UsageExamplePercent);
|
||||
Console.WriteLine(Properties.Resources.CLI_UsageExamplePreset);
|
||||
Console.WriteLine(getString("CLI_UsageExamples"));
|
||||
Console.WriteLine(getString("CLI_UsageExampleHelp"));
|
||||
Console.WriteLine(getString("CLI_UsageExampleDimensions"));
|
||||
Console.WriteLine(getString("CLI_UsageExamplePercent"));
|
||||
Console.WriteLine(getString("CLI_UsageExamplePreset"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
#pragma warning disable IDE0073
|
||||
#pragma warning disable IDE0073, SA1636
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
#pragma warning restore IDE0073, SA1636
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using ImageResizer.Properties;
|
||||
using ImageResizer.Helpers;
|
||||
|
||||
namespace ImageResizer.Models
|
||||
{
|
||||
@@ -15,7 +14,7 @@ namespace ImageResizer.Models
|
||||
[JsonIgnore]
|
||||
public override string Name
|
||||
{
|
||||
get => Resources.Input_Custom;
|
||||
get => ResourceLoaderInstance.GetString("Input_Custom");
|
||||
set { /* no-op */ }
|
||||
}
|
||||
|
||||
|
||||
32
src/modules/imageresizer/ui/Models/ImagingEnums.cs
Normal file
32
src/modules/imageresizer/ui/Models/ImagingEnums.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
// 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.
|
||||
|
||||
namespace ImageResizer.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// PNG interlace option for the encoder.
|
||||
/// Integer values preserve backward compatibility with existing settings JSON.
|
||||
/// </summary>
|
||||
public enum PngInterlaceOption
|
||||
{
|
||||
Default = 0,
|
||||
On = 1,
|
||||
Off = 2,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TIFF compression option for the encoder.
|
||||
/// Integer values preserve backward compatibility with existing settings JSON.
|
||||
/// </summary>
|
||||
public enum TiffCompressOption
|
||||
{
|
||||
Default = 0,
|
||||
None = 1,
|
||||
Ccitt3 = 2,
|
||||
Ccitt4 = 3,
|
||||
Lzw = 4,
|
||||
Rle = 5,
|
||||
Zip = 6,
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma warning disable IDE0073
|
||||
#pragma warning disable IDE0073, SA1636
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
#pragma warning restore IDE0073, SA1636
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
@@ -10,11 +10,9 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.IO.Pipes;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using ImageResizer.Properties;
|
||||
using ImageResizer.Services;
|
||||
|
||||
@@ -40,6 +38,12 @@ namespace ImageResizer.Models
|
||||
_aiSuperResolutionService = null;
|
||||
}
|
||||
|
||||
private static readonly HashSet<string> ValidImageExtensions = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
".bmp", ".dib", ".gif", ".jfif", ".jpe", ".jpeg", ".jpg",
|
||||
".jxr", ".png", ".rle", ".tif", ".tiff", ".wdp",
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Validates if a file path is a supported image format.
|
||||
/// </summary>
|
||||
@@ -57,14 +61,8 @@ namespace ImageResizer.Models
|
||||
return false;
|
||||
}
|
||||
|
||||
var ext = Path.GetExtension(path)?.ToLowerInvariant();
|
||||
var validExtensions = new[]
|
||||
{
|
||||
".bmp", ".dib", ".gif", ".jfif", ".jpe", ".jpeg", ".jpg",
|
||||
".jxr", ".png", ".rle", ".tif", ".tiff", ".wdp",
|
||||
};
|
||||
|
||||
return validExtensions.Contains(ext);
|
||||
var ext = Path.GetExtension(path);
|
||||
return ValidImageExtensions.Contains(ext);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -120,7 +118,7 @@ namespace ImageResizer.Models
|
||||
{
|
||||
string file;
|
||||
|
||||
// Display the read text to the console
|
||||
// Read file paths from the named pipe
|
||||
while ((file = sr.ReadLine()) != null)
|
||||
{
|
||||
if (IsValidImagePath(file))
|
||||
@@ -141,37 +139,35 @@ namespace ImageResizer.Models
|
||||
return FromCliOptions(standardInput, options);
|
||||
}
|
||||
|
||||
public IEnumerable<ResizeError> Process(Action<int, double> reportProgress, CancellationToken cancellationToken)
|
||||
public Task<IEnumerable<ResizeError>> ProcessAsync(Action<int, double> reportProgress, CancellationToken cancellationToken)
|
||||
{
|
||||
// NOTE: Settings.Default is captured once before parallel processing.
|
||||
// Any changes to settings on disk during this batch will NOT be reflected until the next batch.
|
||||
// This improves performance and predictability by avoiding repeated mutex acquisition and behaviour change results in a batch.
|
||||
return Process(reportProgress, Settings.Default, cancellationToken);
|
||||
return ProcessAsync(reportProgress, Settings.Default, cancellationToken);
|
||||
}
|
||||
|
||||
public IEnumerable<ResizeError> Process(Action<int, double> reportProgress, Settings settings, CancellationToken cancellationToken)
|
||||
public async Task<IEnumerable<ResizeError>> ProcessAsync(Action<int, double> reportProgress, Settings settings, CancellationToken cancellationToken)
|
||||
{
|
||||
double total = Files.Count;
|
||||
int completed = 0;
|
||||
var errors = new ConcurrentBag<ResizeError>();
|
||||
|
||||
// TODO: If we ever switch to Windows.Graphics.Imaging, we can get a lot more throughput by using the async
|
||||
// APIs and a custom SynchronizationContext
|
||||
Parallel.ForEach(
|
||||
await Parallel.ForEachAsync(
|
||||
Files,
|
||||
new ParallelOptions
|
||||
{
|
||||
CancellationToken = cancellationToken,
|
||||
},
|
||||
(file, state, i) =>
|
||||
async (file, ct) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Execute(file, settings);
|
||||
await ExecuteAsync(file, settings);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errors.Add(new ResizeError { File = _fileSystem.Path.GetFileName(file), Error = ex.Message });
|
||||
errors.Add(new ResizeError(_fileSystem.Path.GetFileName(file), ex.Message));
|
||||
}
|
||||
|
||||
Interlocked.Increment(ref completed);
|
||||
@@ -181,10 +177,10 @@ namespace ImageResizer.Models
|
||||
return errors;
|
||||
}
|
||||
|
||||
protected virtual void Execute(string file, Settings settings)
|
||||
protected virtual async Task ExecuteAsync(string file, Settings settings)
|
||||
{
|
||||
var aiService = _aiSuperResolutionService ?? NoOpAiSuperResolutionService.Instance;
|
||||
new ResizeOperation(file, DestinationDirectory, settings, aiService).Execute();
|
||||
await new ResizeOperation(file, DestinationDirectory, settings, aiService).ExecuteAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
#pragma warning disable IDE0073
|
||||
#pragma warning disable IDE0073, SA1636
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
#pragma warning restore IDE0073, SA1636
|
||||
|
||||
namespace ImageResizer.Models
|
||||
{
|
||||
public class ResizeError
|
||||
{
|
||||
public string File { get; set; }
|
||||
|
||||
public string Error { get; set; }
|
||||
}
|
||||
public record ResizeError(string File, string Error);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#pragma warning disable IDE0073
|
||||
#pragma warning disable IDE0073, SA1636
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
// Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
#pragma warning restore IDE0073, SA1636
|
||||
|
||||
namespace ImageResizer.Models
|
||||
{
|
||||
|
||||
@@ -1,26 +1,24 @@
|
||||
#pragma warning disable IDE0073
|
||||
#pragma warning disable IDE0073, SA1636
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
#pragma warning restore IDE0073, SA1636
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Abstractions;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
using ImageResizer.Extensions;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Foundation;
|
||||
using Windows.Graphics.Imaging;
|
||||
using Windows.Storage.Streams;
|
||||
using ImageResizer.Helpers;
|
||||
using ImageResizer.Properties;
|
||||
using ImageResizer.Services;
|
||||
using ImageResizer.Utilities;
|
||||
using Microsoft.VisualBasic.FileIO;
|
||||
|
||||
using FileSystem = Microsoft.VisualBasic.FileIO.FileSystem;
|
||||
|
||||
namespace ImageResizer.Models
|
||||
@@ -35,15 +33,24 @@ namespace ImageResizer.Models
|
||||
private readonly IAISuperResolutionService _aiSuperResolutionService;
|
||||
|
||||
// Cache CompositeFormat for AI error message formatting (CA1863)
|
||||
private static readonly CompositeFormat _aiErrorFormat = CompositeFormat.Parse(Resources.Error_AiProcessingFailed);
|
||||
private static CompositeFormat _aiErrorFormat;
|
||||
|
||||
private static CompositeFormat AiErrorFormat =>
|
||||
_aiErrorFormat ??= CompositeFormat.Parse(ResourceLoaderInstance.GetString("Error_AiProcessingFailed"));
|
||||
|
||||
private static readonly string[] RenderingMetadataProperties =
|
||||
[
|
||||
"System.Photo.Orientation",
|
||||
"System.Image.ColorSpace",
|
||||
];
|
||||
|
||||
// Filenames to avoid according to https://learn.microsoft.com/windows/win32/fileio/naming-a-file#file-and-directory-names
|
||||
private static readonly string[] _avoidFilenames =
|
||||
{
|
||||
[
|
||||
"CON", "PRN", "AUX", "NUL",
|
||||
"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
|
||||
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
|
||||
};
|
||||
];
|
||||
|
||||
public ResizeOperation(string file, string destinationDirectory, Settings settings, IAISuperResolutionService aiSuperResolutionService = null)
|
||||
{
|
||||
@@ -53,78 +60,83 @@ namespace ImageResizer.Models
|
||||
_aiSuperResolutionService = aiSuperResolutionService ?? NoOpAiSuperResolutionService.Instance;
|
||||
}
|
||||
|
||||
public void Execute()
|
||||
public async Task ExecuteAsync()
|
||||
{
|
||||
string path;
|
||||
|
||||
using (var inputStream = _fileSystem.File.OpenRead(_file))
|
||||
{
|
||||
var decoder = BitmapDecoder.Create(
|
||||
inputStream,
|
||||
BitmapCreateOptions.PreservePixelFormat,
|
||||
BitmapCacheOption.None);
|
||||
var winrtInputStream = inputStream.AsRandomAccessStream();
|
||||
var decoder = await BitmapDecoder.CreateAsync(winrtInputStream);
|
||||
|
||||
var containerFormat = decoder.CodecInfo.ContainerFormat;
|
||||
|
||||
var encoder = CreateEncoder(containerFormat);
|
||||
|
||||
if (decoder.Metadata != null)
|
||||
// Determine encoder ID from decoder
|
||||
var encoderId = CodecHelper.GetEncoderIdForDecoder(decoder);
|
||||
if (encoderId == null || !CodecHelper.CanEncode(encoderId.Value))
|
||||
{
|
||||
try
|
||||
{
|
||||
encoder.Metadata = decoder.Metadata;
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
}
|
||||
encoderId = CodecHelper.GetEncoderIdFromLegacyGuid(_settings.FallbackEncoder);
|
||||
}
|
||||
|
||||
if (decoder.Palette != null)
|
||||
var encoderGuid = encoderId.Value;
|
||||
|
||||
if (_settings.SelectedSize is AiSize)
|
||||
{
|
||||
encoder.Palette = decoder.Palette;
|
||||
path = await ExecuteAiAsync(decoder, winrtInputStream, encoderGuid);
|
||||
}
|
||||
|
||||
foreach (var originalFrame in decoder.Frames)
|
||||
else
|
||||
{
|
||||
var transformedBitmap = Transform(originalFrame);
|
||||
var originalWidth = (int)decoder.PixelWidth;
|
||||
var originalHeight = (int)decoder.PixelHeight;
|
||||
|
||||
// if the frame was not modified, we should not replace the metadata
|
||||
if (transformedBitmap == originalFrame)
|
||||
var (scaledWidth, scaledHeight, cropBounds, noTransformNeeded) =
|
||||
CalculateDimensions(originalWidth, originalHeight, decoder.DpiX, decoder.DpiY);
|
||||
|
||||
var (outputWidth, outputHeight) = noTransformNeeded
|
||||
? (originalWidth, originalHeight)
|
||||
: cropBounds.HasValue
|
||||
? ((int)cropBounds.Value.Width, (int)cropBounds.Value.Height)
|
||||
: ((int)scaledWidth, (int)scaledHeight);
|
||||
|
||||
path = GetDestinationPath(encoderGuid, outputWidth, outputHeight);
|
||||
_fileSystem.Directory.CreateDirectory(_fileSystem.Path.GetDirectoryName(path));
|
||||
|
||||
using (var outputStream = _fileSystem.File.Open(path, FileMode.CreateNew, FileAccess.ReadWrite))
|
||||
{
|
||||
encoder.Frames.Add(originalFrame);
|
||||
var winrtOutputStream = outputStream.AsRandomAccessStream();
|
||||
await EncodeToStreamAsync(
|
||||
decoder,
|
||||
winrtInputStream,
|
||||
winrtOutputStream,
|
||||
encoderGuid,
|
||||
async (encoder, isTranscode) =>
|
||||
{
|
||||
if (isTranscode)
|
||||
{
|
||||
if (!noTransformNeeded)
|
||||
{
|
||||
encoder.BitmapTransform.ScaledWidth = scaledWidth;
|
||||
encoder.BitmapTransform.ScaledHeight = scaledHeight;
|
||||
encoder.BitmapTransform.InterpolationMode = BitmapInterpolationMode.Fant;
|
||||
|
||||
if (cropBounds.HasValue)
|
||||
{
|
||||
encoder.BitmapTransform.Bounds = cropBounds.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await EncodeFramesAsync(
|
||||
encoder,
|
||||
decoder,
|
||||
scaledWidth,
|
||||
scaledHeight,
|
||||
cropBounds,
|
||||
noTransformNeeded,
|
||||
originalWidth,
|
||||
originalHeight);
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
BitmapMetadata originalMetadata = (BitmapMetadata)originalFrame.Metadata;
|
||||
|
||||
#if DEBUG
|
||||
Debug.WriteLine($"### Processing metadata of file {_file}");
|
||||
originalMetadata.PrintsAllMetadataToDebugOutput();
|
||||
#endif
|
||||
|
||||
var metadata = GetValidMetadata(originalMetadata, transformedBitmap, containerFormat);
|
||||
|
||||
if (_settings.RemoveMetadata && metadata != null)
|
||||
{
|
||||
// strip any metadata that doesn't affect rendering
|
||||
var newMetadata = new BitmapMetadata(metadata.Format);
|
||||
|
||||
metadata.CopyMetadataPropertyTo(newMetadata, "System.Photo.Orientation");
|
||||
metadata.CopyMetadataPropertyTo(newMetadata, "System.Image.ColorSpace");
|
||||
|
||||
metadata = newMetadata;
|
||||
}
|
||||
|
||||
var frame = CreateBitmapFrame(transformedBitmap, metadata);
|
||||
|
||||
encoder.Frames.Add(frame);
|
||||
}
|
||||
}
|
||||
|
||||
path = GetDestinationPath(encoder);
|
||||
_fileSystem.Directory.CreateDirectory(_fileSystem.Path.GetDirectoryName(path));
|
||||
using (var outputStream = _fileSystem.File.Open(path, FileMode.CreateNew, FileAccess.Write))
|
||||
{
|
||||
encoder.Save(outputStream);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,54 +153,271 @@ namespace ImageResizer.Models
|
||||
}
|
||||
}
|
||||
|
||||
private BitmapEncoder CreateEncoder(Guid containerFormat)
|
||||
private async Task<string> ExecuteAiAsync(BitmapDecoder decoder, IRandomAccessStream winrtInputStream, Guid encoderGuid)
|
||||
{
|
||||
var createdEncoder = BitmapEncoder.Create(containerFormat);
|
||||
if (!createdEncoder.CanEncode())
|
||||
try
|
||||
{
|
||||
createdEncoder = BitmapEncoder.Create(_settings.FallbackEncoder);
|
||||
}
|
||||
using var softwareBitmap = await decoder.GetSoftwareBitmapAsync(
|
||||
BitmapPixelFormat.Bgra8,
|
||||
BitmapAlphaMode.Premultiplied);
|
||||
|
||||
ConfigureEncoder(createdEncoder);
|
||||
using var aiResult = _aiSuperResolutionService.ApplySuperResolution(
|
||||
softwareBitmap,
|
||||
_settings.AiSize.Scale,
|
||||
_file);
|
||||
|
||||
return createdEncoder;
|
||||
|
||||
void ConfigureEncoder(BitmapEncoder encoder)
|
||||
{
|
||||
switch (encoder)
|
||||
if (aiResult == null)
|
||||
{
|
||||
case JpegBitmapEncoder jpegEncoder:
|
||||
jpegEncoder.QualityLevel = MathHelpers.Clamp(_settings.JpegQualityLevel, 1, 100);
|
||||
break;
|
||||
|
||||
case PngBitmapEncoder pngBitmapEncoder:
|
||||
pngBitmapEncoder.Interlace = _settings.PngInterlaceOption;
|
||||
break;
|
||||
|
||||
case TiffBitmapEncoder tiffEncoder:
|
||||
tiffEncoder.Compression = _settings.TiffCompressOption;
|
||||
break;
|
||||
throw new InvalidOperationException(ResourceLoaderInstance.GetString("Error_AiConversionFailed"));
|
||||
}
|
||||
|
||||
var outputWidth = aiResult.PixelWidth;
|
||||
var outputHeight = aiResult.PixelHeight;
|
||||
|
||||
var path = GetDestinationPath(encoderGuid, outputWidth, outputHeight);
|
||||
_fileSystem.Directory.CreateDirectory(_fileSystem.Path.GetDirectoryName(path));
|
||||
|
||||
using (var outputStream = _fileSystem.File.Open(path, FileMode.CreateNew, FileAccess.ReadWrite))
|
||||
{
|
||||
var winrtOutputStream = outputStream.AsRandomAccessStream();
|
||||
await EncodeToStreamAsync(
|
||||
decoder,
|
||||
winrtInputStream,
|
||||
winrtOutputStream,
|
||||
encoderGuid,
|
||||
(encoder, _) =>
|
||||
{
|
||||
encoder.SetSoftwareBitmap(aiResult);
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
catch (Exception ex) when (ex is not InvalidOperationException)
|
||||
{
|
||||
var errorMessage = string.Format(CultureInfo.CurrentCulture, AiErrorFormat, ex.Message);
|
||||
throw new InvalidOperationException(errorMessage, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private BitmapSource Transform(BitmapSource source)
|
||||
private async Task EncodeToStreamAsync(
|
||||
BitmapDecoder decoder,
|
||||
IRandomAccessStream inputStream,
|
||||
IRandomAccessStream outputStream,
|
||||
Guid encoderGuid,
|
||||
Func<BitmapEncoder, bool, Task> writeContent)
|
||||
{
|
||||
if (_settings.SelectedSize is AiSize)
|
||||
var decoderEncoderId = CodecHelper.GetEncoderIdForDecoder(decoder);
|
||||
bool canTranscode = !_settings.RemoveMetadata
|
||||
&& decoderEncoderId.HasValue
|
||||
&& decoderEncoderId.Value == encoderGuid;
|
||||
|
||||
if (canTranscode)
|
||||
{
|
||||
return TransformWithAi(source);
|
||||
await TranscodeAsync(decoder, inputStream, outputStream, writeContent);
|
||||
}
|
||||
else
|
||||
{
|
||||
await FreshEncodeAsync(decoder, outputStream, encoderGuid, writeContent);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transcode path: re-encodes pixels via BitmapTransform while preserving all metadata.
|
||||
/// The <paramref name="writeContent"/> callback receives isTranscode=true and should
|
||||
/// configure <see cref="BitmapEncoder.BitmapTransform"/> properties only.
|
||||
/// </summary>
|
||||
private static async Task TranscodeAsync(
|
||||
BitmapDecoder decoder,
|
||||
IRandomAccessStream inputStream,
|
||||
IRandomAccessStream outputStream,
|
||||
Func<BitmapEncoder, bool, Task> writeContent)
|
||||
{
|
||||
inputStream.Seek(0);
|
||||
var encoder = await BitmapEncoder.CreateForTranscodingAsync(outputStream, decoder);
|
||||
await writeContent(encoder, true);
|
||||
|
||||
// Safety net: some JPEG files with large/unusual metadata blocks (e.g. 54 KB
|
||||
// embedded thumbnails) lose EXIF properties during transcode — the WPF equivalent
|
||||
// threw InvalidOperationException on encoder.Metadata = decoder.Metadata for these.
|
||||
// Re-set known critical properties to ensure they survive.
|
||||
await CopyKnownMetadataAsync(decoder, encoder);
|
||||
|
||||
await encoder.FlushAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fresh encoder path: creates a blank encoder and manually writes pixel data.
|
||||
/// Used when metadata must be stripped (RemoveMetadata) or format doesn't match (ICO→PNG).
|
||||
/// The <paramref name="writeContent"/> callback receives isTranscode=false and should
|
||||
/// call <see cref="EncodeFramesAsync"/> or <see cref="BitmapEncoder.SetSoftwareBitmap"/>.
|
||||
/// </summary>
|
||||
private async Task FreshEncodeAsync(
|
||||
BitmapDecoder decoder,
|
||||
IRandomAccessStream outputStream,
|
||||
Guid encoderGuid,
|
||||
Func<BitmapEncoder, bool, Task> writeContent)
|
||||
{
|
||||
// Read rendering-critical metadata before encoding so we can restore it on
|
||||
// the blank encoder. Only needed for RemoveMetadata; format-mismatch files
|
||||
// (e.g. ICO) rarely carry meaningful EXIF data.
|
||||
BitmapPropertySet renderingMetadata = null;
|
||||
if (_settings.RemoveMetadata)
|
||||
{
|
||||
renderingMetadata = await ReadMetadataAsync(decoder, RenderingMetadataProperties);
|
||||
}
|
||||
|
||||
int originalWidth = source.PixelWidth;
|
||||
int originalHeight = source.PixelHeight;
|
||||
var encoder = await CreateFreshEncoderAsync(encoderGuid, outputStream);
|
||||
await writeContent(encoder, false);
|
||||
|
||||
if (renderingMetadata != null)
|
||||
{
|
||||
await WriteMetadataAsync(encoder, renderingMetadata);
|
||||
}
|
||||
|
||||
await encoder.FlushAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decodes each frame, applies the transform, and writes pixel data to the encoder.
|
||||
/// Uses GetPixelDataAsync + SetPixelData for explicit pixel format control — the
|
||||
/// SetSoftwareBitmap API can fail with ArgumentException for some decoder outputs.
|
||||
/// </summary>
|
||||
private static async Task EncodeFramesAsync(
|
||||
BitmapEncoder encoder,
|
||||
BitmapDecoder decoder,
|
||||
uint scaledWidth,
|
||||
uint scaledHeight,
|
||||
BitmapBounds? cropBounds,
|
||||
bool noTransformNeeded,
|
||||
int originalWidth,
|
||||
int originalHeight)
|
||||
{
|
||||
var transform = new BitmapTransform();
|
||||
if (!noTransformNeeded)
|
||||
{
|
||||
transform.ScaledWidth = scaledWidth;
|
||||
transform.ScaledHeight = scaledHeight;
|
||||
transform.InterpolationMode = BitmapInterpolationMode.Fant;
|
||||
|
||||
if (cropBounds.HasValue)
|
||||
{
|
||||
transform.Bounds = cropBounds.Value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
transform.ScaledWidth = (uint)originalWidth;
|
||||
transform.ScaledHeight = (uint)originalHeight;
|
||||
}
|
||||
|
||||
uint outWidth = cropBounds?.Width ?? (noTransformNeeded ? (uint)originalWidth : scaledWidth);
|
||||
uint outHeight = cropBounds?.Height ?? (noTransformNeeded ? (uint)originalHeight : scaledHeight);
|
||||
|
||||
for (uint i = 0; i < decoder.FrameCount; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
{
|
||||
await encoder.GoToNextFrameAsync();
|
||||
}
|
||||
|
||||
var frame = await decoder.GetFrameAsync(i);
|
||||
var pixelData = await frame.GetPixelDataAsync(
|
||||
BitmapPixelFormat.Bgra8,
|
||||
BitmapAlphaMode.Premultiplied,
|
||||
transform,
|
||||
ExifOrientationMode.IgnoreExifOrientation,
|
||||
ColorManagementMode.DoNotColorManage);
|
||||
|
||||
encoder.SetPixelData(
|
||||
BitmapPixelFormat.Bgra8,
|
||||
BitmapAlphaMode.Premultiplied,
|
||||
outWidth,
|
||||
outHeight,
|
||||
frame.DpiX,
|
||||
frame.DpiY,
|
||||
pixelData.DetachPixelData());
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly string[] KnownMetadataProperties =
|
||||
[
|
||||
"System.Photo.DateTaken",
|
||||
"System.Photo.CameraModel",
|
||||
"System.Photo.CameraManufacturer",
|
||||
"System.Photo.Orientation",
|
||||
"System.Image.ColorSpace",
|
||||
"System.Comment",
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Best-effort read of metadata properties from the decoder.
|
||||
/// Returns null if the format doesn't support metadata (e.g. BMP).
|
||||
/// </summary>
|
||||
private static async Task<BitmapPropertySet> ReadMetadataAsync(BitmapDecoder decoder, string[] propertyNames)
|
||||
{
|
||||
try
|
||||
{
|
||||
var props = await decoder.BitmapProperties.GetPropertiesAsync(propertyNames);
|
||||
if (props.Count > 0)
|
||||
{
|
||||
var result = new BitmapPropertySet();
|
||||
foreach (var prop in props)
|
||||
{
|
||||
result[prop.Key] = prop.Value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Some formats (e.g. BMP) don't support property queries.
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Best-effort write of metadata properties to the encoder.
|
||||
/// </summary>
|
||||
private static async Task WriteMetadataAsync(BitmapEncoder encoder, BitmapPropertySet metadata)
|
||||
{
|
||||
if (metadata == null || metadata.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await encoder.BitmapProperties.SetPropertiesAsync(metadata);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Some encoders don't support these properties (e.g. BMP).
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Safety net for the transcode path: re-sets known EXIF properties that
|
||||
/// CreateForTranscodingAsync may silently drop for files with large or
|
||||
/// unusual metadata blocks (see TestMetadataIssue2447.jpg).
|
||||
/// </summary>
|
||||
private static async Task CopyKnownMetadataAsync(BitmapDecoder decoder, BitmapEncoder encoder)
|
||||
{
|
||||
var metadata = await ReadMetadataAsync(decoder, KnownMetadataProperties);
|
||||
await WriteMetadataAsync(encoder, metadata);
|
||||
}
|
||||
|
||||
private (uint ScaledWidth, uint ScaledHeight, BitmapBounds? CropBounds, bool NoTransformNeeded) CalculateDimensions(
|
||||
int originalWidth, int originalHeight, double dpiX, double dpiY)
|
||||
{
|
||||
// Convert from the chosen size unit to pixels, if necessary.
|
||||
double width = _settings.SelectedSize.GetPixelWidth(originalWidth, source.DpiX);
|
||||
double height = _settings.SelectedSize.GetPixelHeight(originalHeight, source.DpiY);
|
||||
double width = _settings.SelectedSize.GetPixelWidth(originalWidth, dpiX);
|
||||
double height = _settings.SelectedSize.GetPixelHeight(originalHeight, dpiY);
|
||||
|
||||
// Swap target width/height dimensions if orientation correction is required.
|
||||
// Ensures that we don't try to fit a landscape image into a portrait box by
|
||||
// distorting it, unless specific Auto/Percent rules are applied.
|
||||
bool canSwapDimensions = _settings.IgnoreOrientation &&
|
||||
!_settings.SelectedSize.HasAuto &&
|
||||
_settings.SelectedSize.Unit != ResizeUnit.Percent;
|
||||
@@ -214,15 +443,11 @@ namespace ImageResizer.Models
|
||||
// Normalize scales based on the chosen Fit/Fill mode.
|
||||
if (_settings.SelectedSize.Fit == ResizeFit.Fit)
|
||||
{
|
||||
// Fit: use the smaller scale to ensure the image fits within the target.
|
||||
scaleX = Math.Min(scaleX, scaleY);
|
||||
scaleY = scaleX;
|
||||
}
|
||||
else if (_settings.SelectedSize.Fit == ResizeFit.Fill)
|
||||
{
|
||||
// Fill: use the larger scale to ensure the target area is fully covered.
|
||||
// This often results in one dimension overflowing, which is handled by
|
||||
// cropping later.
|
||||
scaleX = Math.Max(scaleX, scaleY);
|
||||
scaleY = scaleX;
|
||||
}
|
||||
@@ -230,177 +455,107 @@ namespace ImageResizer.Models
|
||||
// Handle Shrink Only mode.
|
||||
if (_settings.ShrinkOnly && _settings.SelectedSize.Unit != ResizeUnit.Percent)
|
||||
{
|
||||
// Shrink Only mode should never return an image larger than the original.
|
||||
if (scaleX > 1 || scaleY > 1)
|
||||
{
|
||||
return source;
|
||||
return ((uint)originalWidth, (uint)originalHeight, null, true);
|
||||
}
|
||||
|
||||
// Allow for crop-only when in Fill mode.
|
||||
// At this point, the scale is <= 1.0. In Fill mode, it is possible for
|
||||
// the scale to be 1.0 (no resize needed) while the target dimensions are
|
||||
// smaller than the originals, requiring a crop.
|
||||
bool isFillCropRequired = _settings.SelectedSize.Fit == ResizeFit.Fill &&
|
||||
(originalWidth > width || originalHeight > height);
|
||||
|
||||
// If the scale is exactly 1.0 and a crop isn't required, we return the
|
||||
// original image to prevent a re-encode.
|
||||
if (scaleX == 1 && scaleY == 1 && !isFillCropRequired)
|
||||
{
|
||||
return source;
|
||||
return ((uint)originalWidth, (uint)originalHeight, null, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the scaling.
|
||||
var scaledBitmap = new TransformedBitmap(source, new ScaleTransform(scaleX, scaleY));
|
||||
// Calculate scaled dimensions
|
||||
uint scaledWidth = (uint)Math.Max(1, (int)Math.Round(originalWidth * scaleX));
|
||||
uint scaledHeight = (uint)Math.Max(1, (int)Math.Round(originalHeight * scaleY));
|
||||
|
||||
// Apply the centered crop for Fill mode, if necessary. Applies when Fill
|
||||
// mode caused the scaled image to exceed the target dimensions.
|
||||
// Apply the centered crop for Fill mode, if necessary.
|
||||
if (_settings.SelectedSize.Fit == ResizeFit.Fill
|
||||
&& (scaledBitmap.PixelWidth > width
|
||||
|| scaledBitmap.PixelHeight > height))
|
||||
&& (scaledWidth > (uint)width || scaledHeight > (uint)height))
|
||||
{
|
||||
int x = (int)(((originalWidth * scaleX) - width) / 2);
|
||||
int y = (int)(((originalHeight * scaleY) - height) / 2);
|
||||
uint cropX = (uint)(((originalWidth * scaleX) - width) / 2);
|
||||
uint cropY = (uint)(((originalHeight * scaleY) - height) / 2);
|
||||
|
||||
return new CroppedBitmap(scaledBitmap, new Int32Rect(x, y, (int)width, (int)height));
|
||||
}
|
||||
|
||||
return scaledBitmap;
|
||||
}
|
||||
|
||||
private BitmapSource TransformWithAi(BitmapSource source)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = _aiSuperResolutionService.ApplySuperResolution(
|
||||
source,
|
||||
_settings.AiSize.Scale,
|
||||
_file);
|
||||
|
||||
if (result == null)
|
||||
var cropBounds = new BitmapBounds
|
||||
{
|
||||
throw new InvalidOperationException(Properties.Resources.Error_AiConversionFailed);
|
||||
}
|
||||
X = cropX,
|
||||
Y = cropY,
|
||||
Width = (uint)width,
|
||||
Height = (uint)height,
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Wrap the exception with a localized message
|
||||
// This will be caught by ResizeBatch.Process() and displayed to the user
|
||||
var errorMessage = string.Format(CultureInfo.CurrentCulture, _aiErrorFormat, ex.Message);
|
||||
throw new InvalidOperationException(errorMessage, ex);
|
||||
return (scaledWidth, scaledHeight, cropBounds, false);
|
||||
}
|
||||
|
||||
return (scaledWidth, scaledHeight, null, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks original metadata by writing an image containing the given metadata into a memory stream.
|
||||
/// In case of errors, we try to rebuild the metadata object and check again.
|
||||
/// We return null if we were not able to get hold of valid metadata.
|
||||
/// </summary>
|
||||
private BitmapMetadata GetValidMetadata(BitmapMetadata originalMetadata, BitmapSource transformedBitmap, Guid containerFormat)
|
||||
private async Task<BitmapEncoder> CreateFreshEncoderAsync(Guid encoderGuid, IRandomAccessStream outputStream)
|
||||
{
|
||||
if (originalMetadata == null)
|
||||
var propertySet = GetEncoderPropertySet(encoderGuid);
|
||||
return propertySet != null
|
||||
? await BitmapEncoder.CreateAsync(encoderGuid, outputStream, propertySet)
|
||||
: await BitmapEncoder.CreateAsync(encoderGuid, outputStream);
|
||||
}
|
||||
|
||||
private float GetJpegQualityFraction()
|
||||
=> (float)Math.Clamp(_settings.JpegQualityLevel, 1, 100) / 100f;
|
||||
|
||||
private BitmapPropertySet GetEncoderPropertySet(Guid encoderGuid)
|
||||
{
|
||||
if (encoderGuid == BitmapEncoder.JpegEncoderId)
|
||||
{
|
||||
return null;
|
||||
return new BitmapPropertySet
|
||||
{
|
||||
{ "ImageQuality", new BitmapTypedValue(GetJpegQualityFraction(), PropertyType.Single) },
|
||||
};
|
||||
}
|
||||
|
||||
// Check if the original metadata is valid
|
||||
var frameWithOriginalMetadata = CreateBitmapFrame(transformedBitmap, originalMetadata);
|
||||
if (EnsureFrameIsValid(frameWithOriginalMetadata))
|
||||
if (encoderGuid == BitmapEncoder.TiffEncoderId)
|
||||
{
|
||||
return originalMetadata;
|
||||
var compressionMethod = MapTiffCompression(_settings.TiffCompressOption);
|
||||
if (compressionMethod.HasValue)
|
||||
{
|
||||
return new BitmapPropertySet
|
||||
{
|
||||
{ "TiffCompressionMethod", new BitmapTypedValue(compressionMethod.Value, PropertyType.UInt8) },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Original metadata was invalid. We try to rebuild the metadata object from the scratch and discard invalid metadata fields
|
||||
var recreatedMetadata = BuildMetadataFromTheScratch(originalMetadata);
|
||||
var frameWithRecreatedMetadata = CreateBitmapFrame(transformedBitmap, recreatedMetadata);
|
||||
if (EnsureFrameIsValid(frameWithRecreatedMetadata))
|
||||
{
|
||||
return recreatedMetadata;
|
||||
}
|
||||
|
||||
// Seems like we have an invalid metadata object. ImageResizer will fail when trying to write the image to disk. We discard all metadata to be able to save the image.
|
||||
return null;
|
||||
|
||||
// The safest way to check if the metadata object is valid is to call Save() on the encoder.
|
||||
// I tried other ways to check if metadata is valid (like calling Clone() on the metadata object) but this was not reliable resulting in a few github issues.
|
||||
bool EnsureFrameIsValid(BitmapFrame frameToBeChecked)
|
||||
{
|
||||
try
|
||||
{
|
||||
var encoder = CreateEncoder(containerFormat);
|
||||
encoder.Frames.Add(frameToBeChecked);
|
||||
using (var testStream = new MemoryStream())
|
||||
{
|
||||
encoder.Save(testStream);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read all metadata and build up metadata object from the scratch. Discard invalid (unreadable/unwritable) metadata.
|
||||
/// </summary>
|
||||
private static BitmapMetadata BuildMetadataFromTheScratch(BitmapMetadata originalMetadata)
|
||||
private static byte? MapTiffCompression(TiffCompressOption option)
|
||||
{
|
||||
try
|
||||
return option switch
|
||||
{
|
||||
var metadata = new BitmapMetadata(originalMetadata.Format);
|
||||
var listOfMetadata = originalMetadata.GetListOfMetadata();
|
||||
foreach (var (metadataPath, value) in listOfMetadata)
|
||||
{
|
||||
if (value is BitmapMetadata bitmapMetadata)
|
||||
{
|
||||
var innerMetadata = new BitmapMetadata(bitmapMetadata.Format);
|
||||
metadata.SetQuerySafe(metadataPath, innerMetadata);
|
||||
}
|
||||
else
|
||||
{
|
||||
metadata.SetQuerySafe(metadataPath, value);
|
||||
}
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
Debug.WriteLine(ex);
|
||||
|
||||
return null;
|
||||
}
|
||||
TiffCompressOption.None => 1,
|
||||
TiffCompressOption.Ccitt3 => 2,
|
||||
TiffCompressOption.Ccitt4 => 3,
|
||||
TiffCompressOption.Lzw => 4,
|
||||
TiffCompressOption.Rle => 5,
|
||||
TiffCompressOption.Zip => 6,
|
||||
_ => null, // Default: let the encoder decide
|
||||
};
|
||||
}
|
||||
|
||||
private static BitmapFrame CreateBitmapFrame(BitmapSource transformedBitmap, BitmapMetadata metadata)
|
||||
{
|
||||
return BitmapFrame.Create(
|
||||
transformedBitmap,
|
||||
thumbnail: null, /* should be null, see #15413 */
|
||||
metadata,
|
||||
colorContexts: null /* should be null, see #14866 */ );
|
||||
}
|
||||
|
||||
private string GetDestinationPath(BitmapEncoder encoder)
|
||||
private string GetDestinationPath(Guid encoderGuid, int outputPixelWidth, int outputPixelHeight)
|
||||
{
|
||||
var directory = _destinationDirectory ?? _fileSystem.Path.GetDirectoryName(_file);
|
||||
var originalFileName = _fileSystem.Path.GetFileNameWithoutExtension(_file);
|
||||
|
||||
var supportedExtensions = encoder.CodecInfo.FileExtensions.Split(',');
|
||||
var supportedExtensions = CodecHelper.GetSupportedExtensions(encoderGuid);
|
||||
var extension = _fileSystem.Path.GetExtension(_file);
|
||||
if (!supportedExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
extension = supportedExtensions.FirstOrDefault();
|
||||
extension = CodecHelper.GetDefaultExtension(encoderGuid);
|
||||
}
|
||||
|
||||
// Remove directory characters from the size's name.
|
||||
// For AI Size, use the scale display (e.g., "2×") instead of the full name
|
||||
string sizeName = _settings.SelectedSize is AiSize aiSize
|
||||
? aiSize.ScaleDisplay
|
||||
: _settings.SelectedSize.Name;
|
||||
@@ -408,9 +563,8 @@ namespace ImageResizer.Models
|
||||
.Replace('\\', '_')
|
||||
.Replace('/', '_');
|
||||
|
||||
// Using CurrentCulture since this is user facing
|
||||
var selectedWidth = _settings.SelectedSize is AiSize ? encoder.Frames[0].PixelWidth : _settings.SelectedSize.Width;
|
||||
var selectedHeight = _settings.SelectedSize is AiSize ? encoder.Frames[0].PixelHeight : _settings.SelectedSize.Height;
|
||||
var selectedWidth = _settings.SelectedSize is AiSize ? outputPixelWidth : _settings.SelectedSize.Width;
|
||||
var selectedHeight = _settings.SelectedSize is AiSize ? outputPixelHeight : _settings.SelectedSize.Height;
|
||||
var fileName = string.Format(
|
||||
CultureInfo.CurrentCulture,
|
||||
_settings.FileNameFormat,
|
||||
@@ -418,10 +572,9 @@ namespace ImageResizer.Models
|
||||
sizeNameSanitized,
|
||||
selectedWidth,
|
||||
selectedHeight,
|
||||
encoder.Frames[0].PixelWidth,
|
||||
encoder.Frames[0].PixelHeight);
|
||||
outputPixelWidth,
|
||||
outputPixelHeight);
|
||||
|
||||
// Remove invalid characters from the final file name.
|
||||
fileName = fileName
|
||||
.Replace(':', '_')
|
||||
.Replace('*', '_')
|
||||
@@ -431,7 +584,6 @@ namespace ImageResizer.Models
|
||||
.Replace('>', '_')
|
||||
.Replace('|', '_');
|
||||
|
||||
// Avoid creating not recommended filenames
|
||||
if (_avoidFilenames.Contains(fileName.ToUpperInvariant()))
|
||||
{
|
||||
fileName = fileName + "_";
|
||||
|
||||
@@ -1,36 +1,52 @@
|
||||
#pragma warning disable IDE0073
|
||||
#pragma warning disable IDE0073, SA1636
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
// Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
#pragma warning restore IDE0073, SA1636
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using ImageResizer.Helpers;
|
||||
using ImageResizer.Properties;
|
||||
using ManagedCommon;
|
||||
|
||||
namespace ImageResizer.Models
|
||||
{
|
||||
public class ResizeSize : Observable, IHasId
|
||||
public partial class ResizeSize : ObservableObject, IHasId
|
||||
{
|
||||
private static readonly Dictionary<string, string> _tokens = new Dictionary<string, string>
|
||||
private static readonly Dictionary<string, string> _tokenKeys = new Dictionary<string, string>
|
||||
{
|
||||
["$small$"] = Resources.Small,
|
||||
["$medium$"] = Resources.Medium,
|
||||
["$large$"] = Resources.Large,
|
||||
["$phone$"] = Resources.Phone,
|
||||
["$small$"] = "Small",
|
||||
["$medium$"] = "Medium",
|
||||
["$large$"] = "Large",
|
||||
["$phone$"] = "Phone",
|
||||
};
|
||||
|
||||
[ObservableProperty]
|
||||
[JsonPropertyName("Id")]
|
||||
private int _id;
|
||||
|
||||
private string _name;
|
||||
|
||||
[ObservableProperty]
|
||||
[JsonPropertyName("fit")]
|
||||
[NotifyPropertyChangedFor(nameof(ShowHeight))]
|
||||
private ResizeFit _fit = ResizeFit.Fit;
|
||||
|
||||
[ObservableProperty]
|
||||
[JsonPropertyName("width")]
|
||||
private double _width;
|
||||
|
||||
[ObservableProperty]
|
||||
[JsonPropertyName("height")]
|
||||
private double _height;
|
||||
private bool _showHeight = true;
|
||||
|
||||
[ObservableProperty]
|
||||
[JsonPropertyName("unit")]
|
||||
[NotifyPropertyChangedFor(nameof(ShowHeight))]
|
||||
private ResizeUnit _unit = ResizeUnit.Pixel;
|
||||
|
||||
public ResizeSize(int id, string name, ResizeFit fit, double width, double height, ResizeUnit unit)
|
||||
@@ -47,73 +63,18 @@ namespace ImageResizer.Models
|
||||
{
|
||||
}
|
||||
|
||||
[JsonPropertyName("Id")]
|
||||
public int Id
|
||||
{
|
||||
get => _id;
|
||||
set => Set(ref _id, value);
|
||||
}
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public virtual string Name
|
||||
{
|
||||
get => _name;
|
||||
set => Set(ref _name, ReplaceTokens(value));
|
||||
set => SetProperty(ref _name, ReplaceTokens(value));
|
||||
}
|
||||
|
||||
[JsonPropertyName("fit")]
|
||||
public ResizeFit Fit
|
||||
{
|
||||
get => _fit;
|
||||
set
|
||||
{
|
||||
var previousFit = _fit;
|
||||
Set(ref _fit, value);
|
||||
if (!Equals(previousFit, value))
|
||||
{
|
||||
UpdateShowHeight();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[JsonPropertyName("width")]
|
||||
public double Width
|
||||
{
|
||||
get => _width;
|
||||
set => Set(ref _width, value);
|
||||
}
|
||||
|
||||
[JsonPropertyName("height")]
|
||||
public double Height
|
||||
{
|
||||
get => _height;
|
||||
set => Set(ref _height, value);
|
||||
}
|
||||
|
||||
public bool ShowHeight
|
||||
{
|
||||
get => _showHeight;
|
||||
set => Set(ref _showHeight, value);
|
||||
}
|
||||
public bool ShowHeight => Fit == ResizeFit.Stretch || Unit != ResizeUnit.Percent;
|
||||
|
||||
public bool HasAuto
|
||||
=> Width == 0 || Height == 0 || double.IsNaN(Width) || double.IsNaN(Height);
|
||||
|
||||
[JsonPropertyName("unit")]
|
||||
public ResizeUnit Unit
|
||||
{
|
||||
get => _unit;
|
||||
set
|
||||
{
|
||||
var previousUnit = _unit;
|
||||
Set(ref _unit, value);
|
||||
if (!Equals(previousUnit, value))
|
||||
{
|
||||
UpdateShowHeight();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public double GetPixelWidth(int originalWidth, double dpi)
|
||||
=> ConvertToPixels(Width, Unit, originalWidth, dpi);
|
||||
|
||||
@@ -127,15 +88,10 @@ namespace ImageResizer.Models
|
||||
dpi);
|
||||
|
||||
private static string ReplaceTokens(string text)
|
||||
=> (text != null && _tokens.TryGetValue(text, out var result))
|
||||
? result
|
||||
=> text != null && _tokenKeys.TryGetValue(text, out var key)
|
||||
? ResourceLoaderInstance.GetString(key)
|
||||
: text;
|
||||
|
||||
private void UpdateShowHeight()
|
||||
{
|
||||
ShowHeight = Fit == ResizeFit.Stretch || Unit != ResizeUnit.Percent;
|
||||
}
|
||||
|
||||
private double ConvertToPixels(double value, ResizeUnit unit, int originalValue, double dpi)
|
||||
{
|
||||
if (value == 0 || double.IsNaN(value))
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#pragma warning disable IDE0073
|
||||
#pragma warning disable IDE0073, SA1636
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
// Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
#pragma warning restore IDE0073, SA1636
|
||||
|
||||
namespace ImageResizer.Models
|
||||
{
|
||||
|
||||
27
src/modules/imageresizer/ui/Program.cs
Normal file
27
src/modules/imageresizer/ui/Program.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
// 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 Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
namespace ImageResizer
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
[STAThread]
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
WinRT.ComWrappersSupport.InitializeComWrappers();
|
||||
Application.Start((p) =>
|
||||
{
|
||||
var context = new DispatcherQueueSynchronizationContext(
|
||||
DispatcherQueue.GetForCurrentThread());
|
||||
SynchronizationContext.SetSynchronizationContext(context);
|
||||
_ = new App();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("ImageResizer.Test")]
|
||||
1152
src/modules/imageresizer/ui/Properties/Resources.Designer.cs
generated
1152
src/modules/imageresizer/ui/Properties/Resources.Designer.cs
generated
File diff suppressed because it is too large
Load Diff
75
src/modules/imageresizer/ui/Properties/Resources.cs
Normal file
75
src/modules/imageresizer/ui/Properties/Resources.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
// 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 ImageResizer.Helpers;
|
||||
|
||||
namespace ImageResizer.Properties
|
||||
{
|
||||
/// <summary>
|
||||
/// Resource accessor class for compatibility with CLI code and tests.
|
||||
/// Wraps ResourceLoader for resource string access.
|
||||
/// </summary>
|
||||
internal static class Resources
|
||||
{
|
||||
// Size names (used by tests and ResizeSize token replacement)
|
||||
public static string Small => ResourceLoaderInstance.GetString("Small");
|
||||
|
||||
public static string Medium => ResourceLoaderInstance.GetString("Medium");
|
||||
|
||||
public static string Large => ResourceLoaderInstance.GetString("Large");
|
||||
|
||||
public static string Phone => ResourceLoaderInstance.GetString("Phone");
|
||||
|
||||
// Input page resources
|
||||
public static string Input_Custom => ResourceLoaderInstance.GetString("Input_Custom");
|
||||
|
||||
// Validation messages
|
||||
public static string ValueMustBeBetween => ResourceLoaderInstance.GetString("ValueMustBeBetween");
|
||||
|
||||
// CLI options
|
||||
public static string CLI_Option_Destination => ResourceLoaderInstance.GetString("CLI_Option_Destination");
|
||||
|
||||
public static string CLI_Option_FileName => ResourceLoaderInstance.GetString("CLI_Option_FileName");
|
||||
|
||||
public static string CLI_Option_Files => ResourceLoaderInstance.GetString("CLI_Option_Files");
|
||||
|
||||
public static string CLI_Option_Fit => ResourceLoaderInstance.GetString("CLI_Option_Fit");
|
||||
|
||||
public static string CLI_Option_Height => ResourceLoaderInstance.GetString("CLI_Option_Height");
|
||||
|
||||
public static string CLI_Option_Help => ResourceLoaderInstance.GetString("CLI_Option_Help");
|
||||
|
||||
public static string CLI_Option_IgnoreOrientation => ResourceLoaderInstance.GetString("CLI_Option_IgnoreOrientation");
|
||||
|
||||
public static string CLI_Option_KeepDateModified => ResourceLoaderInstance.GetString("CLI_Option_KeepDateModified");
|
||||
|
||||
public static string CLI_Option_Quality => ResourceLoaderInstance.GetString("CLI_Option_Quality");
|
||||
|
||||
public static string CLI_Option_Replace => ResourceLoaderInstance.GetString("CLI_Option_Replace");
|
||||
|
||||
public static string CLI_Option_ShowConfig => ResourceLoaderInstance.GetString("CLI_Option_ShowConfig");
|
||||
|
||||
public static string CLI_Option_ShrinkOnly => ResourceLoaderInstance.GetString("CLI_Option_ShrinkOnly");
|
||||
|
||||
public static string CLI_Option_RemoveMetadata => ResourceLoaderInstance.GetString("CLI_Option_RemoveMetadata");
|
||||
|
||||
public static string CLI_Option_Size => ResourceLoaderInstance.GetString("CLI_Option_Size");
|
||||
|
||||
public static string CLI_Option_Unit => ResourceLoaderInstance.GetString("CLI_Option_Unit");
|
||||
|
||||
public static string CLI_Option_Width => ResourceLoaderInstance.GetString("CLI_Option_Width");
|
||||
|
||||
public static string CLI_ProcessingFiles => ResourceLoaderInstance.GetString("CLI_ProcessingFiles");
|
||||
|
||||
public static string CLI_ProgressFormat => ResourceLoaderInstance.GetString("CLI_ProgressFormat");
|
||||
|
||||
public static string CLI_CompletedWithErrors => ResourceLoaderInstance.GetString("CLI_CompletedWithErrors");
|
||||
|
||||
public static string CLI_AllFilesProcessed => ResourceLoaderInstance.GetString("CLI_AllFilesProcessed");
|
||||
|
||||
public static string CLI_WarningInvalidSizeIndex => ResourceLoaderInstance.GetString("CLI_WarningInvalidSizeIndex");
|
||||
|
||||
public static string CLI_NoInputFiles => ResourceLoaderInstance.GetString("CLI_NoInputFiles");
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma warning disable IDE0073
|
||||
#pragma warning disable IDE0073, SA1636
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
#pragma warning restore IDE0073, SA1636
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
@@ -16,13 +16,10 @@ using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
using System.Threading;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
using ImageResizer.Helpers;
|
||||
using ImageResizer.Models;
|
||||
using ImageResizer.Services;
|
||||
using ImageResizer.ViewModels;
|
||||
using ManagedCommon;
|
||||
using Microsoft.UI.Dispatching;
|
||||
|
||||
namespace ImageResizer.Properties
|
||||
{
|
||||
@@ -42,14 +39,21 @@ namespace ImageResizer.Properties
|
||||
private static readonly JsonSerializerOptions _jsonSerializerOptions = new JsonSerializerOptions
|
||||
{
|
||||
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals,
|
||||
PropertyNameCaseInsensitive = true,
|
||||
WriteIndented = true,
|
||||
TypeInfoResolver = new DefaultJsonTypeInfoResolver(),
|
||||
};
|
||||
|
||||
private static readonly CompositeFormat ValueMustBeBetween = System.Text.CompositeFormat.Parse(Properties.Resources.ValueMustBeBetween);
|
||||
// Cached UI thread DispatcherQueue for cross-thread property change notifications
|
||||
private static DispatcherQueue _uiDispatcherQueue;
|
||||
|
||||
// Used to synchronize access to the settings.json file
|
||||
private static Mutex _jsonMutex = new Mutex();
|
||||
private static CompositeFormat _valueMustBeBetween;
|
||||
|
||||
private static CompositeFormat ValueMustBeBetween =>
|
||||
_valueMustBeBetween ??= System.Text.CompositeFormat.Parse(ResourceLoaderInstance.GetString("ValueMustBeBetween"));
|
||||
|
||||
// Used to synchronize access to the settings.json file (in-process only)
|
||||
private static readonly System.Threading.Lock _jsonSyncLock = new();
|
||||
private static string _settingsPath = _fileSystem.Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData), "Microsoft", "PowerToys", "Image Resizer", "settings.json");
|
||||
private string _fileNameFormat;
|
||||
private bool _shrinkOnly;
|
||||
@@ -74,8 +78,8 @@ namespace ImageResizer.Properties
|
||||
IgnoreOrientation = true;
|
||||
RemoveMetadata = false;
|
||||
JpegQualityLevel = 90;
|
||||
PngInterlaceOption = System.Windows.Media.Imaging.PngInterlaceOption.Default;
|
||||
TiffCompressOption = System.Windows.Media.Imaging.TiffCompressOption.Default;
|
||||
PngInterlaceOption = Models.PngInterlaceOption.Default;
|
||||
TiffCompressOption = Models.TiffCompressOption.Default;
|
||||
FileName = "%1 (%2)";
|
||||
Sizes = new ObservableCollection<ResizeSize>
|
||||
{
|
||||
@@ -87,32 +91,25 @@ namespace ImageResizer.Properties
|
||||
KeepDateModified = false;
|
||||
FallbackEncoder = new System.Guid("19e4a5aa-5662-4fc5-a0c0-1758028e1057");
|
||||
CustomSize = new CustomSize(ResizeFit.Fit, 1024, 640, ResizeUnit.Pixel);
|
||||
AiSize = new AiSize(2); // Initialize with default scale of 2
|
||||
AiSize = new AiSize(2);
|
||||
AllSizes = new AllSizesCollection(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates the SelectedSizeIndex to ensure it's within the valid range.
|
||||
/// This handles cross-device migration where settings saved on ARM64 with AI selected
|
||||
/// are loaded on non-ARM64 devices.
|
||||
/// </summary>
|
||||
private void ValidateSelectedSizeIndex()
|
||||
{
|
||||
// Index structure: 0 to Sizes.Count-1 (regular), Sizes.Count (CustomSize), Sizes.Count+1 (AiSize)
|
||||
var maxIndex = ImageResizer.App.AiAvailabilityState == AiAvailabilityState.NotSupported
|
||||
? Sizes.Count // CustomSize only
|
||||
: Sizes.Count + 1; // CustomSize + AiSize
|
||||
? Sizes.Count
|
||||
: Sizes.Count + 1;
|
||||
|
||||
if (_selectedSizeIndex > maxIndex)
|
||||
{
|
||||
_selectedSizeIndex = 0; // Reset to first size
|
||||
_selectedSizeIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public IEnumerable<ResizeSize> AllSizes { get; set; }
|
||||
|
||||
// Using OrdinalIgnoreCase since this is internal and used for comparison with symbols
|
||||
public string FileNameFormat
|
||||
=> _fileNameFormat
|
||||
?? (_fileNameFormat = FileName
|
||||
@@ -144,7 +141,6 @@ namespace ImageResizer.Properties
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback to CustomSize when index is out of range or AI is not available
|
||||
return CustomSize;
|
||||
}
|
||||
}
|
||||
@@ -168,13 +164,7 @@ namespace ImageResizer.Properties
|
||||
}
|
||||
}
|
||||
|
||||
string IDataErrorInfo.Error
|
||||
{
|
||||
get
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
string IDataErrorInfo.Error => string.Empty;
|
||||
|
||||
string IDataErrorInfo.this[string columnName]
|
||||
{
|
||||
@@ -187,7 +177,6 @@ namespace ImageResizer.Properties
|
||||
|
||||
if (JpegQualityLevel < 1 || JpegQualityLevel > 100)
|
||||
{
|
||||
// Using CurrentCulture since this is user facing
|
||||
return string.Format(CultureInfo.CurrentCulture, ValueMustBeBetween, 1, 100);
|
||||
}
|
||||
|
||||
@@ -217,26 +206,20 @@ namespace ImageResizer.Properties
|
||||
if (e.PropertyName == nameof(Models.CustomSize))
|
||||
{
|
||||
_customSize = settings.CustomSize;
|
||||
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
else if (e.PropertyName == nameof(Models.AiSize))
|
||||
{
|
||||
_aiSize = settings.AiSize;
|
||||
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||
}
|
||||
else if (e.PropertyName == nameof(Sizes))
|
||||
{
|
||||
var oldSizes = _sizes;
|
||||
|
||||
oldSizes.CollectionChanged -= HandleCollectionChanged;
|
||||
((INotifyPropertyChanged)oldSizes).PropertyChanged -= HandlePropertyChanged;
|
||||
|
||||
_sizes = settings.Sizes;
|
||||
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||
|
||||
_sizes.CollectionChanged += HandleCollectionChanged;
|
||||
((INotifyPropertyChanged)_sizes).PropertyChanged += HandlePropertyChanged;
|
||||
}
|
||||
@@ -244,7 +227,6 @@ namespace ImageResizer.Properties
|
||||
}
|
||||
|
||||
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public int Count
|
||||
@@ -291,7 +273,6 @@ namespace ImageResizer.Properties
|
||||
private class AllSizesEnumerator : IEnumerator<ResizeSize>
|
||||
{
|
||||
private readonly AllSizesCollection _list;
|
||||
|
||||
private int _index = -1;
|
||||
|
||||
public AllSizesEnumerator(AllSizesCollection list)
|
||||
@@ -334,9 +315,13 @@ namespace ImageResizer.Properties
|
||||
get => _selectedSizeIndex;
|
||||
set
|
||||
{
|
||||
if (_selectedSizeIndex == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_selectedSizeIndex = value;
|
||||
NotifyPropertyChanged();
|
||||
NotifyPropertyChanged(nameof(SelectedSize));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -376,15 +361,6 @@ namespace ImageResizer.Properties
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether resizing images removes any metadata that doesn't affect rendering.
|
||||
/// Default is false.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Preserved Metadata:
|
||||
/// System.Photo.Orientation,
|
||||
/// System.Image.ColorSpace
|
||||
/// </remarks>
|
||||
[JsonConverter(typeof(WrappedJsonValueConverter))]
|
||||
[JsonPropertyName("imageresizer_removeMetadata")]
|
||||
public bool RemoveMetadata
|
||||
@@ -505,6 +481,15 @@ namespace ImageResizer.Properties
|
||||
|
||||
public static string SettingsPath { get => _settingsPath; set => _settingsPath = value; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the UI DispatcherQueue for cross-thread property change notifications.
|
||||
/// Must be called from the UI thread during app startup.
|
||||
/// </summary>
|
||||
public static void InitializeDispatcher()
|
||||
{
|
||||
_uiDispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private void NotifyPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
|
||||
@@ -514,16 +499,23 @@ namespace ImageResizer.Properties
|
||||
|
||||
public void Save()
|
||||
{
|
||||
_jsonMutex.WaitOne();
|
||||
lock (_jsonSyncLock)
|
||||
{
|
||||
SaveCore();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes current settings to disk. Must be called under <see cref="_jsonSyncLock"/>.
|
||||
/// </summary>
|
||||
private void SaveCore()
|
||||
{
|
||||
string jsonData = JsonSerializer.Serialize(new SettingsWrapper() { Properties = this }, _jsonSerializerOptions);
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
IFileInfo file = _fileSystem.FileInfo.New(SettingsPath);
|
||||
file.Directory.Create();
|
||||
|
||||
// write string to file
|
||||
_fileSystem.File.WriteAllText(SettingsPath, jsonData);
|
||||
_jsonMutex.ReleaseMutex();
|
||||
}
|
||||
|
||||
public void Reload()
|
||||
@@ -536,35 +528,46 @@ namespace ImageResizer.Properties
|
||||
_fileSystem.Directory.Move(oldSettingsDir, settingsDir);
|
||||
}
|
||||
|
||||
_jsonMutex.WaitOne();
|
||||
if (!_fileSystem.File.Exists(SettingsPath))
|
||||
// Read and deserialize under lock; ReloadCore runs outside the lock
|
||||
// because jsonSettings is an in-memory snapshot with no file I/O.
|
||||
Settings jsonSettings;
|
||||
lock (_jsonSyncLock)
|
||||
{
|
||||
_jsonMutex.ReleaseMutex();
|
||||
Save();
|
||||
return;
|
||||
if (!_fileSystem.File.Exists(SettingsPath))
|
||||
{
|
||||
SaveCore();
|
||||
return;
|
||||
}
|
||||
|
||||
string jsonData = _fileSystem.File.ReadAllText(SettingsPath);
|
||||
jsonSettings = new Settings();
|
||||
try
|
||||
{
|
||||
jsonSettings = JsonSerializer.Deserialize<SettingsWrapper>(jsonData, _jsonSerializerOptions)?.Properties;
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
Logger.LogError($"Failed to parse settings JSON, using defaults: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
string jsonData = _fileSystem.File.ReadAllText(SettingsPath);
|
||||
var jsonSettings = new Settings();
|
||||
try
|
||||
// Apply deserialized snapshot to live properties on the UI thread.
|
||||
if (_uiDispatcherQueue != null)
|
||||
{
|
||||
jsonSettings = JsonSerializer.Deserialize<SettingsWrapper>(jsonData, _jsonSerializerOptions)?.Properties;
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
}
|
||||
|
||||
if (App.Current?.Dispatcher != null)
|
||||
{
|
||||
// Needs to be called on the App UI thread as the properties are bound to the UI.
|
||||
App.Current.Dispatcher.Invoke(() => ReloadCore(jsonSettings));
|
||||
if (_uiDispatcherQueue.HasThreadAccess)
|
||||
{
|
||||
ReloadCore(jsonSettings);
|
||||
}
|
||||
else
|
||||
{
|
||||
_uiDispatcherQueue.TryEnqueue(() => ReloadCore(jsonSettings));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No UI context (unit tests or CLI mode) — call directly.
|
||||
ReloadCore(jsonSettings);
|
||||
}
|
||||
|
||||
_jsonMutex.ReleaseMutex();
|
||||
}
|
||||
|
||||
private void ReloadCore(Settings jsonSettings)
|
||||
@@ -580,20 +583,16 @@ namespace ImageResizer.Properties
|
||||
KeepDateModified = jsonSettings.KeepDateModified;
|
||||
FallbackEncoder = jsonSettings.FallbackEncoder;
|
||||
CustomSize = jsonSettings.CustomSize;
|
||||
AiSize = jsonSettings.AiSize ?? new AiSize(InputViewModel.DefaultAiScale);
|
||||
AiSize = jsonSettings.AiSize ?? new AiSize(2);
|
||||
SelectedSizeIndex = jsonSettings.SelectedSizeIndex;
|
||||
|
||||
if (jsonSettings.Sizes.Count > 0)
|
||||
{
|
||||
Sizes.Clear();
|
||||
Sizes.AddRange(jsonSettings.Sizes);
|
||||
|
||||
// Ensure Ids are unique and handle missing Ids
|
||||
IdRecoveryHelper.RecoverInvalidIds(Sizes);
|
||||
}
|
||||
|
||||
// Validate SelectedSizeIndex after Sizes collection has been updated
|
||||
// This handles cross-device migration (e.g., ARM64 -> non-ARM64)
|
||||
ValidateSelectedSizeIndex();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma warning disable IDE0073
|
||||
#pragma warning disable IDE0073, SA1636
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
#pragma warning restore IDE0073, SA1636
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma warning disable IDE0073
|
||||
#pragma warning disable IDE0073, SA1636
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
#pragma warning restore IDE0073, SA1636
|
||||
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma warning disable IDE0073
|
||||
#pragma warning disable IDE0073, SA1636
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
#pragma warning restore IDE0073, SA1636
|
||||
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Windows.Graphics.Imaging;
|
||||
|
||||
namespace ImageResizer.Services
|
||||
{
|
||||
public interface IAISuperResolutionService : IDisposable
|
||||
{
|
||||
BitmapSource ApplySuperResolution(BitmapSource source, int scale, string filePath);
|
||||
SoftwareBitmap ApplySuperResolution(SoftwareBitmap source, int scale, string filePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// 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.Windows.Media.Imaging;
|
||||
using Windows.Graphics.Imaging;
|
||||
|
||||
namespace ImageResizer.Services
|
||||
{
|
||||
@@ -14,7 +14,7 @@ namespace ImageResizer.Services
|
||||
{
|
||||
}
|
||||
|
||||
public BitmapSource ApplySuperResolution(BitmapSource source, int scale, string filePath)
|
||||
public SoftwareBitmap ApplySuperResolution(SoftwareBitmap source, int scale, string filePath)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
|
||||
@@ -3,13 +3,8 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using ManagedCommon;
|
||||
using Microsoft.Windows.AI;
|
||||
using Microsoft.Windows.AI.Imaging;
|
||||
using Windows.Graphics.Imaging;
|
||||
@@ -47,8 +42,9 @@ namespace ImageResizer.Services
|
||||
|
||||
return new WinAiSuperResolutionService(imageScaler);
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Failed to create AI super resolution service: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -59,39 +55,28 @@ namespace ImageResizer.Services
|
||||
{
|
||||
return ImageScaler.GetReadyState();
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
// If we can't get the state, treat it as disabled by user
|
||||
// The caller should check if it's Ready or NotReady
|
||||
Logger.LogWarning($"Failed to get AI model ready state: {ex.Message}");
|
||||
return AIFeatureReadyState.DisabledByUser;
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<AIFeatureReadyResult> EnsureModelReadyAsync(IProgress<double> progress = null)
|
||||
public static async Task<AIFeatureReadyResult> EnsureModelReadyAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var operation = ImageScaler.EnsureReadyAsync();
|
||||
|
||||
// Register progress handler if provided
|
||||
if (progress != null)
|
||||
{
|
||||
operation.Progress = (asyncInfo, progressValue) =>
|
||||
{
|
||||
// progressValue is a double representing completion percentage (0.0 to 1.0 or 0 to 100)
|
||||
progress.Report(progressValue);
|
||||
};
|
||||
}
|
||||
|
||||
return await operation;
|
||||
return await ImageScaler.EnsureReadyAsync();
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Failed to ensure AI model ready: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public BitmapSource ApplySuperResolution(BitmapSource source, int scale, string filePath)
|
||||
public SoftwareBitmap ApplySuperResolution(SoftwareBitmap source, int scale, string filePath)
|
||||
{
|
||||
if (source == null || _disposed)
|
||||
{
|
||||
@@ -102,19 +87,12 @@ namespace ImageResizer.Services
|
||||
// Currently not used by the ImageScaler API
|
||||
try
|
||||
{
|
||||
// Convert WPF BitmapSource to WinRT SoftwareBitmap
|
||||
var softwareBitmap = ConvertBitmapSourceToSoftwareBitmap(source);
|
||||
if (softwareBitmap == null)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
|
||||
// Calculate target dimensions
|
||||
var newWidth = softwareBitmap.PixelWidth * scale;
|
||||
var newHeight = softwareBitmap.PixelHeight * scale;
|
||||
var newWidth = source.PixelWidth * scale;
|
||||
var newHeight = source.PixelHeight * scale;
|
||||
|
||||
// Apply super resolution with thread-safe access
|
||||
// _usageLock protects concurrent access from Parallel.ForEach threads
|
||||
// _usageLock protects concurrent access from Parallel.ForEachAsync threads
|
||||
SoftwareBitmap scaledBitmap;
|
||||
lock (_usageLock)
|
||||
{
|
||||
@@ -123,120 +101,19 @@ namespace ImageResizer.Services
|
||||
return source;
|
||||
}
|
||||
|
||||
scaledBitmap = _imageScaler.ScaleSoftwareBitmap(softwareBitmap, newWidth, newHeight);
|
||||
scaledBitmap = _imageScaler.ScaleSoftwareBitmap(source, newWidth, newHeight);
|
||||
}
|
||||
|
||||
if (scaledBitmap == null)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
|
||||
// Convert back to WPF BitmapSource
|
||||
return ConvertSoftwareBitmapToBitmapSource(scaledBitmap);
|
||||
return scaledBitmap ?? source;
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Any error, return original image gracefully
|
||||
Logger.LogError($"AI super resolution failed for {filePath}: {ex.Message}");
|
||||
return source;
|
||||
}
|
||||
}
|
||||
|
||||
private static SoftwareBitmap ConvertBitmapSourceToSoftwareBitmap(BitmapSource bitmapSource)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Ensure the bitmap is in a compatible format
|
||||
var convertedBitmap = new FormatConvertedBitmap();
|
||||
convertedBitmap.BeginInit();
|
||||
convertedBitmap.Source = bitmapSource;
|
||||
convertedBitmap.DestinationFormat = PixelFormats.Bgra32;
|
||||
convertedBitmap.EndInit();
|
||||
|
||||
int width = convertedBitmap.PixelWidth;
|
||||
int height = convertedBitmap.PixelHeight;
|
||||
int stride = width * 4; // 4 bytes per pixel for Bgra32
|
||||
byte[] pixels = new byte[height * stride];
|
||||
|
||||
convertedBitmap.CopyPixels(pixels, stride, 0);
|
||||
|
||||
// Create SoftwareBitmap from pixel data
|
||||
var softwareBitmap = new SoftwareBitmap(
|
||||
BitmapPixelFormat.Bgra8,
|
||||
width,
|
||||
height,
|
||||
BitmapAlphaMode.Premultiplied);
|
||||
|
||||
using (var buffer = softwareBitmap.LockBuffer(BitmapBufferAccessMode.Write))
|
||||
using (var reference = buffer.CreateReference())
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
((IMemoryBufferByteAccess)reference).GetBuffer(out byte* dataInBytes, out uint capacity);
|
||||
System.Runtime.InteropServices.Marshal.Copy(pixels, 0, (IntPtr)dataInBytes, pixels.Length);
|
||||
}
|
||||
}
|
||||
|
||||
return softwareBitmap;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static BitmapSource ConvertSoftwareBitmapToBitmapSource(SoftwareBitmap softwareBitmap)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Convert to Bgra8 format if needed
|
||||
var convertedBitmap = SoftwareBitmap.Convert(
|
||||
softwareBitmap,
|
||||
BitmapPixelFormat.Bgra8,
|
||||
BitmapAlphaMode.Premultiplied);
|
||||
|
||||
int width = convertedBitmap.PixelWidth;
|
||||
int height = convertedBitmap.PixelHeight;
|
||||
int stride = width * 4; // 4 bytes per pixel for Bgra8
|
||||
byte[] pixels = new byte[height * stride];
|
||||
|
||||
using (var buffer = convertedBitmap.LockBuffer(BitmapBufferAccessMode.Read))
|
||||
using (var reference = buffer.CreateReference())
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
((IMemoryBufferByteAccess)reference).GetBuffer(out byte* dataInBytes, out uint capacity);
|
||||
System.Runtime.InteropServices.Marshal.Copy((IntPtr)dataInBytes, pixels, 0, pixels.Length);
|
||||
}
|
||||
}
|
||||
|
||||
// Create WPF BitmapSource from pixel data
|
||||
var wpfBitmap = BitmapSource.Create(
|
||||
width,
|
||||
height,
|
||||
96, // DPI X
|
||||
96, // DPI Y
|
||||
PixelFormats.Bgra32,
|
||||
null,
|
||||
pixels,
|
||||
stride);
|
||||
|
||||
wpfBitmap.Freeze(); // Make it thread-safe
|
||||
return wpfBitmap;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
[ComImport]
|
||||
[Guid("5B0D3235-4DBA-4D44-865E-8F1D0E4FD04D")]
|
||||
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
|
||||
private interface IMemoryBufferByteAccess
|
||||
{
|
||||
unsafe void GetBuffer(out byte* buffer, out uint capacity);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
@@ -117,18 +117,21 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="AllFilesFilter" xml:space="preserve">
|
||||
<value>All Files</value>
|
||||
<data name="ImageResizer" xml:space="preserve">
|
||||
<value>Image Resizer</value>
|
||||
<comment>Product name, do not loc</comment>
|
||||
</data>
|
||||
<data name="Cancel" xml:space="preserve">
|
||||
<data name="Cancel.Text" xml:space="preserve">
|
||||
<value>Cancel</value>
|
||||
</data>
|
||||
<data name="Height" xml:space="preserve">
|
||||
<value>Height</value>
|
||||
</data>
|
||||
<data name="ImageResizer" xml:space="preserve">
|
||||
<value>Image Resizer</value>
|
||||
<comment>Product name, do not loc</comment>
|
||||
<data name="Width" xml:space="preserve">
|
||||
<value>Width</value>
|
||||
</data>
|
||||
<data name="Unit" xml:space="preserve">
|
||||
<value>Unit</value>
|
||||
</data>
|
||||
<data name="Input_Auto" xml:space="preserve">
|
||||
<value>(auto)</value>
|
||||
@@ -139,94 +142,118 @@
|
||||
<data name="Input_Custom" xml:space="preserve">
|
||||
<value>Custom</value>
|
||||
</data>
|
||||
<data name="Input_IgnoreOrientation" xml:space="preserve">
|
||||
<value>Ignore the _orientation of pictures</value>
|
||||
<data name="Input_IgnoreOrientation.Text" xml:space="preserve">
|
||||
<value>Ignore the orientation of pictures</value>
|
||||
</data>
|
||||
<data name="Input_GifWarning" xml:space="preserve">
|
||||
<data name="Input_GifWarning.Message" xml:space="preserve">
|
||||
<value>Gif files with animations may not be correctly resized.</value>
|
||||
</data>
|
||||
<data name="Input_Replace" xml:space="preserve">
|
||||
<value>Ov_erwrite files</value>
|
||||
<data name="Input_Replace.Text" xml:space="preserve">
|
||||
<value>Overwrite files</value>
|
||||
</data>
|
||||
<data name="Input_Resize" xml:space="preserve">
|
||||
<data name="Input_Resize.Text" xml:space="preserve">
|
||||
<value>Resize</value>
|
||||
</data>
|
||||
<data name="Input_ShrinkOnly" xml:space="preserve">
|
||||
<value>_Make pictures smaller but not larger</value>
|
||||
<data name="Input_ShrinkOnly.Text" xml:space="preserve">
|
||||
<value>Make pictures smaller but not larger</value>
|
||||
</data>
|
||||
<data name="Large" xml:space="preserve">
|
||||
<value>Large</value>
|
||||
<data name="Input_RemoveMetadata.Text" xml:space="preserve">
|
||||
<value>Remove metadata that doesn't affect rendering</value>
|
||||
</data>
|
||||
<data name="Image_Sizes" xml:space="preserve">
|
||||
<value>Image sizes</value>
|
||||
</data>
|
||||
<data name="Resize_Tooltip" xml:space="preserve">
|
||||
<value>Resize pictures</value>
|
||||
</data>
|
||||
<data name="Resize_Type" xml:space="preserve">
|
||||
<value>Resize type</value>
|
||||
</data>
|
||||
<data name="Open_settings.Text" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
</data>
|
||||
<data name="Open_settings_button.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
</data>
|
||||
<data name="Input_AiSuperResolution" xml:space="preserve">
|
||||
<value>Super resolution</value>
|
||||
</data>
|
||||
<data name="Input_AiSuperResolutionDescription" xml:space="preserve">
|
||||
<value>Upscale images using on-device AI</value>
|
||||
</data>
|
||||
<data name="Input_AiUnknownSize" xml:space="preserve">
|
||||
<value>Unavailable</value>
|
||||
</data>
|
||||
<data name="Input_AiScaleFormat" xml:space="preserve">
|
||||
<value>{0}×</value>
|
||||
</data>
|
||||
<data name="Input_AiScaleLabel" xml:space="preserve">
|
||||
<value>Scale</value>
|
||||
</data>
|
||||
<data name="Input_AiCurrentLabel.Text" xml:space="preserve">
|
||||
<value>Current:</value>
|
||||
</data>
|
||||
<data name="Input_AiNewLabel.Text" xml:space="preserve">
|
||||
<value>New:</value>
|
||||
</data>
|
||||
<data name="Input_AiModelChecking" xml:space="preserve">
|
||||
<value>Checking AI model availability...</value>
|
||||
</data>
|
||||
<data name="Input_AiModelNotAvailable" xml:space="preserve">
|
||||
<value>AI model not downloaded. Click Download to get started.</value>
|
||||
</data>
|
||||
<data name="Input_AiModelDisabledByUser" xml:space="preserve">
|
||||
<value>AI feature is disabled by system settings.</value>
|
||||
</data>
|
||||
<data name="Input_AiModelNotSupported" xml:space="preserve">
|
||||
<value>AI feature is not supported on this system.</value>
|
||||
</data>
|
||||
<data name="Input_AiModelDownloading" xml:space="preserve">
|
||||
<value>Downloading AI model...</value>
|
||||
</data>
|
||||
<data name="Input_AiModelDownloadFailed" xml:space="preserve">
|
||||
<value>Failed to download AI model. Please try again.</value>
|
||||
</data>
|
||||
<data name="Input_AiModelDownloadButton.Text" xml:space="preserve">
|
||||
<value>Download</value>
|
||||
</data>
|
||||
<data name="Error_AiProcessingFailed" xml:space="preserve">
|
||||
<value>AI super resolution processing failed: {0}</value>
|
||||
</data>
|
||||
<data name="Error_AiConversionFailed" xml:space="preserve">
|
||||
<value>Failed to convert image format for AI processing.</value>
|
||||
</data>
|
||||
<data name="Error_AiScalingFailed" xml:space="preserve">
|
||||
<value>AI scaling operation failed.</value>
|
||||
</data>
|
||||
<data name="Progress_MainInstruction.Text" xml:space="preserve">
|
||||
<value>Resizing your pictures...</value>
|
||||
</data>
|
||||
<data name="Progress_Stop.Text" xml:space="preserve">
|
||||
<value>Stop</value>
|
||||
</data>
|
||||
<data name="Progress_TimeRemaining" xml:space="preserve">
|
||||
<value>About {0} remaining.</value>
|
||||
<comment>"About" = Approximately</comment>
|
||||
</data>
|
||||
<data name="Results_MainInstruction.Text" xml:space="preserve">
|
||||
<value>Can't resize the following pictures</value>
|
||||
</data>
|
||||
<data name="Results_Close.Text" xml:space="preserve">
|
||||
<value>Close</value>
|
||||
</data>
|
||||
<data name="Small" xml:space="preserve">
|
||||
<value>Small</value>
|
||||
</data>
|
||||
<data name="Medium" xml:space="preserve">
|
||||
<value>Medium</value>
|
||||
</data>
|
||||
<data name="OK" xml:space="preserve">
|
||||
<value>OK</value>
|
||||
</data>
|
||||
<data name="OK_Tooltip" xml:space="preserve">
|
||||
<value>Apply settings</value>
|
||||
<data name="Large" xml:space="preserve">
|
||||
<value>Large</value>
|
||||
</data>
|
||||
<data name="Phone" xml:space="preserve">
|
||||
<value>Phone</value>
|
||||
</data>
|
||||
<data name="PictureFilter" xml:space="preserve">
|
||||
<value>All Picture Files</value>
|
||||
</data>
|
||||
<data name="PngInterlaceOption_Default" xml:space="preserve">
|
||||
<value>(Default)</value>
|
||||
</data>
|
||||
<data name="PngInterlaceOption_Off" xml:space="preserve">
|
||||
<value>Off</value>
|
||||
</data>
|
||||
<data name="PngInterlaceOption_On" xml:space="preserve">
|
||||
<value>On</value>
|
||||
</data>
|
||||
<data name="Progress_MainInstruction" xml:space="preserve">
|
||||
<value>Resizing your pictures...</value>
|
||||
</data>
|
||||
<data name="Progress_Stop" xml:space="preserve">
|
||||
<value>Stop</value>
|
||||
</data>
|
||||
<data name="Progress_TimeRemaining_HourMinute" xml:space="preserve">
|
||||
<value>About {0} hour, {1} minute remaining.</value>
|
||||
<comment>"About" = Approximately, not "on the subject of"</comment>
|
||||
</data>
|
||||
<data name="Progress_TimeRemaining_HourMinutes" xml:space="preserve">
|
||||
<value>About {0} hour, {1} minutes remaining.</value>
|
||||
<comment>"About" = Approximately, not "on the subject of"</comment>
|
||||
</data>
|
||||
<data name="Progress_TimeRemaining_HoursMinute" xml:space="preserve">
|
||||
<value>About {0} hours, {1} minute remaining.</value>
|
||||
<comment>"About" = Approximately, not "on the subject of"</comment>
|
||||
</data>
|
||||
<data name="Progress_TimeRemaining_HoursMinutes" xml:space="preserve">
|
||||
<value>About {0} hours, {1} minutes remaining.</value>
|
||||
<comment>"About" = Approximately, not "on the subject of"</comment>
|
||||
</data>
|
||||
<data name="Progress_TimeRemaining_MinuteSecond" xml:space="preserve">
|
||||
<value>About {1} minute, {2} second remaining.</value>
|
||||
<comment>"About" = Approximately, not "on the subject of"</comment>
|
||||
</data>
|
||||
<data name="Progress_TimeRemaining_MinuteSeconds" xml:space="preserve">
|
||||
<value>About {1} minute, {2} seconds remaining.</value>
|
||||
<comment>"About" = Approximately, not "on the subject of"</comment>
|
||||
</data>
|
||||
<data name="Progress_TimeRemaining_MinutesSecond" xml:space="preserve">
|
||||
<value>About {1} minutes, {2} second remaining.</value>
|
||||
<comment>"About" = Approximately, not "on the subject of"</comment>
|
||||
</data>
|
||||
<data name="Progress_TimeRemaining_MinutesSeconds" xml:space="preserve">
|
||||
<value>About {1} minutes, {2} seconds remaining.</value>
|
||||
<comment>"About" = Approximately, not "on the subject of"</comment>
|
||||
</data>
|
||||
<data name="Progress_TimeRemaining_Second" xml:space="preserve">
|
||||
<value>About {2} second remaining.</value>
|
||||
<comment>"About" = Approximately, not "on the subject of"</comment>
|
||||
</data>
|
||||
<data name="Progress_TimeRemaining_Seconds" xml:space="preserve">
|
||||
<value>About {2} seconds remaining.</value>
|
||||
<comment>"About" = Approximately, not "on the subject of"</comment>
|
||||
</data>
|
||||
<data name="ResizeFit_Fill" xml:space="preserve">
|
||||
<value>Fill</value>
|
||||
</data>
|
||||
@@ -257,98 +284,15 @@
|
||||
<data name="ResizeUnit_Pixel" xml:space="preserve">
|
||||
<value>Pixels</value>
|
||||
</data>
|
||||
<data name="Resize_Tooltip" xml:space="preserve">
|
||||
<value>Resize pictures</value>
|
||||
<data name="PngInterlaceOption_Default" xml:space="preserve">
|
||||
<value>(Default)</value>
|
||||
</data>
|
||||
<data name="Resize_Type" xml:space="preserve">
|
||||
<value>Resize type</value>
|
||||
<data name="PngInterlaceOption_Off" xml:space="preserve">
|
||||
<value>Off</value>
|
||||
</data>
|
||||
<data name="Results_Close" xml:space="preserve">
|
||||
<value>Close</value>
|
||||
<data name="PngInterlaceOption_On" xml:space="preserve">
|
||||
<value>On</value>
|
||||
</data>
|
||||
<data name="Results_MainInstruction" xml:space="preserve">
|
||||
<value>Can't resize the following pictures</value>
|
||||
</data>
|
||||
<data name="Small" xml:space="preserve">
|
||||
<value>Small</value>
|
||||
</data>
|
||||
<data name="Unit" xml:space="preserve">
|
||||
<value>Unit</value>
|
||||
</data>
|
||||
<data name="ValueMustBeBetween" xml:space="preserve">
|
||||
<value>Value must be between '{0}' and '{1}'.</value>
|
||||
</data>
|
||||
<data name="Version" xml:space="preserve">
|
||||
<value>Version</value>
|
||||
</data>
|
||||
<data name="Width" xml:space="preserve">
|
||||
<value>Width</value>
|
||||
</data>
|
||||
<data name="Open_settings" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
</data>
|
||||
<data name="Input_RemoveMetadata" xml:space="preserve">
|
||||
<value>Remove meta_data that doesn't affect rendering</value>
|
||||
</data>
|
||||
<data name="Image_Sizes" xml:space="preserve">
|
||||
<value>Image sizes</value>
|
||||
</data>
|
||||
<data name="Input_ShrinkOnly.Content" xml:space="preserve">
|
||||
<value>_Make pictures smaller but not larger</value>
|
||||
</data>
|
||||
<data name="Input_AiSuperResolution" xml:space="preserve">
|
||||
<value>Super resolution</value>
|
||||
</data>
|
||||
<data name="Input_AiUnknownSize" xml:space="preserve">
|
||||
<value>Unavailable</value>
|
||||
</data>
|
||||
<data name="Input_AiScaleFormat" xml:space="preserve">
|
||||
<value>{0}×</value>
|
||||
</data>
|
||||
<data name="Input_AiScaleLabel" xml:space="preserve">
|
||||
<value>Scale</value>
|
||||
</data>
|
||||
<data name="Input_AiCurrentLabel" xml:space="preserve">
|
||||
<value>Current:</value>
|
||||
</data>
|
||||
<data name="Input_AiNewLabel" xml:space="preserve">
|
||||
<value>New:</value>
|
||||
</data>
|
||||
<data name="Input_AiModelChecking" xml:space="preserve">
|
||||
<value>Checking AI model availability...</value>
|
||||
</data>
|
||||
<data name="Input_AiModelNotAvailable" xml:space="preserve">
|
||||
<value>AI model not downloaded. Click Download to get started.</value>
|
||||
</data>
|
||||
<data name="Input_AiModelDisabledByUser" xml:space="preserve">
|
||||
<value>AI feature is disabled by system settings.</value>
|
||||
</data>
|
||||
<data name="Input_AiModelNotSupported" xml:space="preserve">
|
||||
<value>AI feature is not supported on this system.</value>
|
||||
</data>
|
||||
<data name="Input_AiModelDownloading" xml:space="preserve">
|
||||
<value>Downloading AI model...</value>
|
||||
</data>
|
||||
<data name="Input_AiModelDownloadFailed" xml:space="preserve">
|
||||
<value>Failed to download AI model. Please try again.</value>
|
||||
</data>
|
||||
<data name="Input_AiModelDownloadButton" xml:space="preserve">
|
||||
<value>Download</value>
|
||||
</data>
|
||||
<data name="Error_AiProcessingFailed" xml:space="preserve">
|
||||
<value>AI super resolution processing failed: {0}</value>
|
||||
</data>
|
||||
<data name="Error_AiConversionFailed" xml:space="preserve">
|
||||
<value>Failed to convert image format for AI processing.</value>
|
||||
</data>
|
||||
<data name="Error_AiScalingFailed" xml:space="preserve">
|
||||
<value>AI scaling operation failed.</value>
|
||||
</data>
|
||||
<data name="Input_AiSuperResolutionDescription" xml:space="preserve">
|
||||
<value>Upscale images using on-device AI</value>
|
||||
</data>
|
||||
|
||||
<!-- CLI Processing messages -->
|
||||
<data name="CLI_ProcessingFiles" xml:space="preserve">
|
||||
<value>Processing {0} file(s)...</value>
|
||||
</data>
|
||||
@@ -367,8 +311,6 @@
|
||||
<data name="CLI_NoInputFiles" xml:space="preserve">
|
||||
<value>No input files or pipe specified. Showing usage.</value>
|
||||
</data>
|
||||
|
||||
<!-- CLI Config display -->
|
||||
<data name="CLI_ConfigTitle" xml:space="preserve">
|
||||
<value>ImageResizer - Current Configuration</value>
|
||||
</data>
|
||||
@@ -423,8 +365,6 @@
|
||||
<data name="CLI_ConfigCustomSelected" xml:space="preserve">
|
||||
<value> [Custom]* {0}x{1} {2} ({3})</value>
|
||||
</data>
|
||||
|
||||
<!-- CLI Usage help -->
|
||||
<data name="CLI_UsageTitle" xml:space="preserve">
|
||||
<value>ImageResizer - PowerToys Image Resizer CLI</value>
|
||||
</data>
|
||||
@@ -449,8 +389,6 @@
|
||||
<data name="CLI_UsageExamplePreset" xml:space="preserve">
|
||||
<value> PowerToys.ImageResizerCLI.exe --size 0 -d "C:\Output" photo.png</value>
|
||||
</data>
|
||||
|
||||
<!-- CLI Option Descriptions -->
|
||||
<data name="CLI_Option_Destination" xml:space="preserve">
|
||||
<value>Set destination directory</value>
|
||||
</data>
|
||||
@@ -499,4 +437,10 @@
|
||||
<data name="CLI_Option_Width" xml:space="preserve">
|
||||
<value>Set width</value>
|
||||
</data>
|
||||
<data name="ValueMustBeBetween" xml:space="preserve">
|
||||
<value>Value must be between '{0}' and '{1}'.</value>
|
||||
</data>
|
||||
<data name="TitleBarTitle.Title" xml:space="preserve">
|
||||
<value>Image Resizer</value>
|
||||
</data>
|
||||
</root>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user