Compare commits

..

2 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
eb199d26f6 Fix PowerToys Run shortcut conflict display when module is disabled
Co-authored-by: yeelam-gordon <73506701+yeelam-gordon@users.noreply.github.com>
2025-08-29 08:46:37 +00:00
copilot-swe-agent[bot]
6ffa8d5827 Initial plan 2025-08-29 08:33:59 +00:00
131 changed files with 713 additions and 16996 deletions

View File

@@ -70,7 +70,6 @@ APPMODEL
APPNAME
appref
appsettings
appsfeatures
appwindow
appwiz
appxpackage
@@ -1316,7 +1315,6 @@ PRODUCTVERSION
Progman
programdata
projectname
projitems
PROPERTYKEY
Propset
PROPVARIANT
@@ -1396,7 +1394,6 @@ regkey
regroot
regsvr
REINSTALLMODE
releaseblog
reloadable
Relogger
remappings

View File

@@ -1,20 +0,0 @@
name: Manual Batch Issue Deduplication
on:
workflow_dispatch: # Only runs when manually triggered
jobs:
batch-deduplicate:
runs-on: ubuntu-latest
steps:
- name: Batch Deduplicate Issues
uses: pelikhan/action-genai-issue-dedup@v0
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
label-duplicate: "potential duplicate"
comment-duplicate: true
close-duplicate: false
batch-size: 100
since: '2019-05-05T00:00:00Z' # Process issues dating back to 2019
duplicate-comment-template: "This issue appears to be a duplicate of #{duplicate_issue_number}."
# Add other action-specific inputs if needed

View File

@@ -5,13 +5,11 @@ MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "runner", "src\runner\runner.vcxproj", "{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}"
ProjectSection(ProjectDependencies) = postProject
{031AC72E-FA28-4AB7-B690-6F7B9C28AA73} = {031AC72E-FA28-4AB7-B690-6F7B9C28AA73}
{08E71C67-6A7E-4CA1-B04E-2FB336410BAC} = {08E71C67-6A7E-4CA1-B04E-2FB336410BAC}
{0B43679E-EDFA-4DA0-AD30-F4628B308B1B} = {0B43679E-EDFA-4DA0-AD30-F4628B308B1B}
{0B593A6C-4143-4337-860E-DB5710FB87DB} = {0B593A6C-4143-4337-860E-DB5710FB87DB}
{17DA04DF-E393-4397-9CF0-84DABE11032E} = {17DA04DF-E393-4397-9CF0-84DABE11032E}
{217DF501-135C-4E38-BFC8-99D4821032EA} = {217DF501-135C-4E38-BFC8-99D4821032EA}
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34} = {2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}
{38177D56-6AD1-4ADF-88C9-2843A7932166} = {38177D56-6AD1-4ADF-88C9-2843A7932166}
{48804216-2A0E-4168-A6D8-9CD068D14227} = {48804216-2A0E-4168-A6D8-9CD068D14227}
{51920F1F-C28C-4ADF-8660-4238766796C2} = {51920F1F-C28C-4ADF-8660-4238766796C2}
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481} = {5CCC8468-DEC8-4D36-99D4-5C891BEBD481}
@@ -803,12 +801,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.WebSea
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.CmdPal.Ext.Shell.UnitTests", "src\modules\cmdpal\Tests\Microsoft.CmdPal.Ext.Shell.UnitTests\Microsoft.CmdPal.Ext.Shell.UnitTests.csproj", "{E816D7B4-4688-4ECB-97CC-3D8E798F3833}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LightSwitch", "LightSwitch", "{5B201255-53C8-490B-A34F-01F05D48A477}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LightSwitchModuleInterface", "src\modules\LightSwitch\LightSwitchModuleInterface\LightSwitchModuleInterface.vcxproj", "{38177D56-6AD1-4ADF-88C9-2843A7932166}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LightModeService", "src\modules\LightSwitch\LightSwitchService\LightSwitchService.vcxproj", "{08E71C67-6A7E-4CA1-B04E-2FB336410BAC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64
@@ -2927,22 +2919,6 @@ Global
{E816D7B4-4688-4ECB-97CC-3D8E798F3833}.Release|ARM64.Build.0 = Release|ARM64
{E816D7B4-4688-4ECB-97CC-3D8E798F3833}.Release|x64.ActiveCfg = Release|x64
{E816D7B4-4688-4ECB-97CC-3D8E798F3833}.Release|x64.Build.0 = Release|x64
{38177D56-6AD1-4ADF-88C9-2843A7932166}.Debug|ARM64.ActiveCfg = Debug|ARM64
{38177D56-6AD1-4ADF-88C9-2843A7932166}.Debug|ARM64.Build.0 = Debug|ARM64
{38177D56-6AD1-4ADF-88C9-2843A7932166}.Debug|x64.ActiveCfg = Debug|x64
{38177D56-6AD1-4ADF-88C9-2843A7932166}.Debug|x64.Build.0 = Debug|x64
{38177D56-6AD1-4ADF-88C9-2843A7932166}.Release|ARM64.ActiveCfg = Release|ARM64
{38177D56-6AD1-4ADF-88C9-2843A7932166}.Release|ARM64.Build.0 = Release|ARM64
{38177D56-6AD1-4ADF-88C9-2843A7932166}.Release|x64.ActiveCfg = Release|x64
{38177D56-6AD1-4ADF-88C9-2843A7932166}.Release|x64.Build.0 = Release|x64
{08E71C67-6A7E-4CA1-B04E-2FB336410BAC}.Debug|ARM64.ActiveCfg = Debug|ARM64
{08E71C67-6A7E-4CA1-B04E-2FB336410BAC}.Debug|ARM64.Build.0 = Debug|ARM64
{08E71C67-6A7E-4CA1-B04E-2FB336410BAC}.Debug|x64.ActiveCfg = Debug|x64
{08E71C67-6A7E-4CA1-B04E-2FB336410BAC}.Debug|x64.Build.0 = Debug|x64
{08E71C67-6A7E-4CA1-B04E-2FB336410BAC}.Release|ARM64.ActiveCfg = Release|ARM64
{08E71C67-6A7E-4CA1-B04E-2FB336410BAC}.Release|ARM64.Build.0 = Release|ARM64
{08E71C67-6A7E-4CA1-B04E-2FB336410BAC}.Release|x64.ActiveCfg = Release|x64
{08E71C67-6A7E-4CA1-B04E-2FB336410BAC}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -3261,9 +3237,6 @@ Global
{E816D7B3-4688-4ECB-97CC-3D8E798F3832} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
{E816D7B2-4688-4ECB-97CC-3D8E798F3831} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
{E816D7B4-4688-4ECB-97CC-3D8E798F3833} = {8EF25507-2575-4ADE-BF7E-D23376903AB8}
{5B201255-53C8-490B-A34F-01F05D48A477} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{38177D56-6AD1-4ADF-88C9-2843A7932166} = {5B201255-53C8-490B-A34F-01F05D48A477}
{08E71C67-6A7E-4CA1-B04E-2FB336410BAC} = {5B201255-53C8-490B-A34F-01F05D48A477}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}

199
README.md
View File

@@ -35,19 +35,19 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
Go to the [Microsoft PowerToys GitHub releases page][github-release-link] and click on `Assets` at the bottom to show the files available in the release. Please use the appropriate PowerToys installer that matches your machine's architecture and install scope. For most, it is `x64` and per-user.
<!-- items that need to be updated release to release -->
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.95%22
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.94%22
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.94.0/PowerToysUserSetup-0.94.0-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.94.0/PowerToysUserSetup-0.94.0-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.94.0/PowerToysSetup-0.94.0-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.94.0/PowerToysSetup-0.94.0-arm64.exe
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.94%22
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.93%22
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.93.0/PowerToysUserSetup-0.93.0-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.93.0/PowerToysUserSetup-0.93.0-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.93.0/PowerToysSetup-0.93.0-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.93.0/PowerToysSetup-0.93.0-arm64.exe
| Description | Filename |
|----------------|----------|
| Per user - x64 | [PowerToysUserSetup-0.94.0-x64.exe][ptUserX64] |
| Per user - ARM64 | [PowerToysUserSetup-0.94.0-arm64.exe][ptUserArm64] |
| Machine wide - x64 | [PowerToysSetup-0.94.0-x64.exe][ptMachineX64] |
| Machine wide - ARM64 | [PowerToysSetup-0.94.0-arm64.exe][ptMachineArm64] |
| Per user - x64 | [PowerToysUserSetup-0.93.0-x64.exe][ptUserX64] |
| Per user - ARM64 | [PowerToysUserSetup-0.93.0-arm64.exe][ptUserArm64] |
| Machine wide - x64 | [PowerToysSetup-0.93.0-x64.exe][ptMachineX64] |
| Machine wide - ARM64 | [PowerToysSetup-0.93.0-arm64.exe][ptMachineArm64] |
This is our preferred method.
@@ -93,145 +93,118 @@ For guidance on developing for PowerToys, please read the [developer docs](./doc
Our [prioritized roadmap][roadmap] of features and utilities that the core team is focusing on.
### 0.94 - Sep 2025 Update
### 0.93 - Aug 2025 Update
In this release, we focused on new features, stability, optimization improvements, and automation.
For an in-depth look at the latest changes, visit the [release blog](https://aka.ms/powertoys-releaseblog).
**✨Highlights**
- PowerToys Settings added a Settings search with fuzzy matching, suggestions, a results page, and UX polish to make finding options faster.
- A comprehensive hotkey conflict detection system was introduced in Settings to surface and help resolve conflicting shortcuts. Note that the default hotkey settings (Win+Ctrl+Shift+T, Win+Ctrl+V, Win+Ctrl+T, Win+Shift+T) may overlap with existing Windows system shortcuts. This is expected. You can resolve the conflict by assigning different hotkeys.
- Mouse Utilities added a “Gliding cursor” accessibility feature to Mouse Pointer Crosshairs for singlebutton cursor movement and clicking. Thanks [@mikehall-ms](https://github.com/mikehall-ms)!
- The installer was upgraded to WiX 5 after WiX 3 reached end-of-life; this move improved installer security, reliability, and community support.
- Tons of bug fixes and improvements for Command Palette, including visual updates and new support for filters on ListPages (handy for extension developers).
- Hosts Editor now has a “No leading spaces” option so active host entries can start at column 0 even if others are disabled. Thanks [@mohammed-saalim](https://github.com/mohammed-saalim)!
- Context menu registration was moved from the installer to runtime to avoid loading disabled modules (runtime registrations).
- Quick Accent now supports Maltese, and frequently used accents appear first (and are remembered across sessions). Thanks [@rovercoder](https://github.com/rovercoder)! [@davidegiacometti](https://github.com/davidegiacometti)!
### Always On Top
- Fixed the border hover cursor so it shows the arrow instead of the wait cursor. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- PowerToys settings debuts a modern, card-based dashboard with clearer descriptions and faster navigation for a streamlined user experience.
- Command Palette had over 99 issues resolved, including bringing back Clipboard History, adding context menu shortcuts, pinning favorite apps, and supporting history in Run.
- Command Palette reduced its startup memory usage by ~15%, load time by ~40%, built-in extensions loading time by ~70%, and installation size by ~55%—all due to using the full Ahead-of-Time (AOT) compilation mode in Windows App SDK.
- Peek now supports instant previews and embedded thumbnails for Binary G-code (.bgcode) 3D printing files, making it easy to inspect models at a glance. Thanks [@pedrolamas](https://github.com/pedrolamas)!
- Mouse Utilities introduces a new spotlight highlighting mode that dims the screen and draws attention to your cursor, perfect for presentations.
- Test coverage improvements for multiple PowerToys modules including Command Palette, Advanced Paste, Peek, Text Extractor, and PowerRename — ensuring better reliability and quality, with over 600 new unit tests (mostly for Command Palette) and doubled UI automation coverage.
### Command Palette
- Applied single-click activation only to pointer input; keyboard always activates immediately. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Let context menus open at the cursor by removing window-bound constraints. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Made error messages clearer with timestamps, HRESULTs, and full details for easier diagnosis. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Prevented crashes and improved robustness when updating providers without commands. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Ensured the Settings window reliably comes to the front when opened. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Replaced the Clipboard History icon with a colorful Fluent icon. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Hardened ContentIcon to avoid duplicate parenting and improve diagnostics. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Standardized null checks using C# pattern matching for safer behavior.
- Improved accessibility by focusing the activation shortcut dialog and making text reachable. Thanks [@chatasweetie](https://github.com/chatasweetie)!
- Moved the extension SDK to a stable Windows SDK and cleaned up message namespaces.
- Added path shortcuts: ~ to home, and / or \\ to system root, plus UNC support. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Fixed a race in cancellation handling to avoid InvalidOperationException. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Aligned separator styling with WinUI 3 for consistent visuals. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Added ARM64 PDBs to the Extensions SDK NuGet for better debugging.
- Added single-select filters to DynamicListPage and updated Windows Services sample.
- Updated main page placeholder text to better describe what can be searched. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Removed explicit WinAppSDK/WebView2 dependencies from toolkit and API. Thanks [@rluengen](https://github.com/rluengen)!
- Added a local keyboard hook to handle the GoBack key reliably. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Propagated alias changes safely and resolved conflicts across view models.
- Allowed providers to override Dispose with a virtual method.
- Fixed memory leaks by cleaning up removed or cancelled list items.
- Sorted DateTime extension results by relevance for better usability.
- Reduced search text “jiggling” by avoiding redundant change notifications.
- Centralized automation notifications in a UIHelper for better accessibility. Thanks [@chatasweetie](https://github.com/chatasweetie)!
- Preserved Adaptive Card action types during trimming via DynamicDependency.
- Added an acrylic backdrop and refined styling to the context menu. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Prevented disposed pages and Settings windows from handling stale messages. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Made the extension API easier to evolve without breaking clients.
- Added “evil” sample pages to help reproduce tricky bugs.
- Fixed WinGet trim-safety issues by replacing LINQ with manual iteration.
- Cancelled stale list fetches to avoid older results overwriting newer ones in CmdPal.
- Ensured screen readers are notified when the selected item in the list changes for better accessibility.
- Fixed command title changes not being properly notified to screen readers. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Made icon controls excluded from keyboard navigation by default for better accessibility. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Improved UI design with better text sizing and alignment.
- Fixed keyboard shortcuts to work better in text boxes and context menus.
- Added right-click context menus with critical command styling and separators.
- Improved various context menu issues, improving item selection, handling of long titles, search bar text scaling, initial item behavior, and primary button functionality.
- Fixed context menu crashes with better type handling.
- Fixed "Reload" command to work with both uppercase and lowercase letters.
- Added mouse back button support for easier navigation. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Fixed Alt+Left Arrow navigation not working when search box contains text. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Updated back button tooltip to show keyboard shortcut information. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Fixed Command Palette window not appearing properly when activated. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Fixed Command Palette window staying hidden from taskbar after File Explorer restarts. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Fixed window focus not returning to previous app properly.
- Fixed Command Palette window to always appear on top when shown and move to bottom when hidden. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Fixed window hiding to properly work on UI thread. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Fixed crashes and improved stability with better synchronization of Command list updates. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Improved extension disposal with better error handling to prevent crashes. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Improved stability by fixing a UI threading issue when loading more results, preventing possible crashes and ensuring the loading state resets if loading fails. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Enhanced icon loading stability with better exception handling. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Added thread safety to recent commands to prevent crashes. Thanks [@MaoShengelia](https://github.com/MaoShengelia)!
- Fixed acrylic (frosted glass) system backdrop display issues by ensuring proper UI thread handling. Thanks [@jiripolasek](https://github.com/jiripolasek)!
### Command Palette extensions
- Improved empty states and ranking logic for multiple extensions. Thanks [@htcfreek](https://github.com/htcfreek)!
- Added app icons to the All Apps "Run" context command when available.
- Restored missing builtin icons by standardizing extension dependencies.
- Unblocked local deployment by adding WinAppSDK to two sample extensions.
### Hosts File Editor
- Added a "No leading spaces" option so active hosts entries can start at column 0 even when others are disabled. Thanks [@mohammed-saalim](https://github.com/mohammed-saalim)!
### Image Resizer
- Fixed Image Resizer localization by installing satellite resources under the WinUI 3 apps culture path.
- Added settings to each provider to control which fallback commands are enabled. Thanks [@jiripolasek](https://github.com/jiripolasek)! for fixing a regression in this feature.
- Added sample code showing how Command Palette extensions can track when their pages are loaded or unloaded. [Check it out here](./src/modules/cmdpal/ext/SamplePagesExtension/OnLoadPage.cs).
- Fixed *Calculator* to accept regular spaces in numbers that use space separators. Thanks [@PesBandi](https://github.com/PesBandi)!
- Added a new setting to *Calculator* to make "Copy" the primary button (replacing “Save”) and enable "Close on Enter", streamlining the workflow. Thanks [@PesBandi](https://github.com/PesBandi)!
- Improved *Apps* indexing error handling and removed obsolete code. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Prevented apps from showing in search when the *Apps* extension is disabled. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Added ability to pin/unpin *Apps* using Ctrl+P shortcut.
- Added keyboard shortcuts to the *Apps* context menu items for faster access.
- Added all file context menu options to the *Apps* items context menu, making all file actions available there for better functionality.
- Streamlined All *Apps* extension settings by removing redundant descriptions, making the UI clearer.
- Added command history to the *Run* page for easier access to previous commands.
- Fixed directory path handling in *Run* fallback for better file navigation.
- Fixed URL fallback item hiding properly in *Web Search* extension when search query becomes invalid. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Added proper empty state message for *Web Search* extension when no results found. Thanks [@jiripolasek](https://github.com/jiripolasek)!
- Added fallback command to *Windows Settings* extension for better search results.
- Re-enabled *Clipboard History* feature with proper window handling.
- Improved *Add Bookmark* extension to automatically detect file, folder, or URL types without manual input.
- Updated terminology from "Kill process" to "End task" in *Window Walker* for consistency with Windows.
- Fixed minor grammar error in SamplePagesExtension code comments. Thanks [@purofle](https://github.com/purofle)!
### Mouse Utilities
- Introduced "Gliding cursor" to control the pointer and click with a single hotkey for better accessibility. Thanks [@mikehall-ms](https://github.com/mikehall-ms)!
### Mouse Without Borders
- Blocked Easy Mouse from switching machines during fullscreen apps, with an allow-list for exceptions. Thanks [@dot-tb](https://github.com/dot-tb)!
- Added a new spotlight highlighting mode that creates a large transparent circle around your cursor with a backdrop effect, providing an alternative to the traditional circle highlight. Perfect for presentations where you want to focus attention on a specific area while dimming the rest of the screen.
### Peek
- Added Visual Studio shared project file types to XML preview and fixed bgcode handler registration. Thanks [@rezanid](https://github.com/rezanid)!
- Fixes bgcode preview handler registration and events for reliable previews. Thanks [@pedrolamas](https://github.com/pedrolamas)!
### PowerRename
- Changed the Explorer accelerator key to PowErRename to avoid clashing with the New menu. Thanks [@aaron-ni](https://github.com/aaron-ni)!
- Added preview and thumbnail support for Binary G-code (.bgcode) files used in 3D printing. You can now see embedded thumbnails and preview these compressed 3D printing files directly in Peek and File Explorer. Thanks [@pedrolamas](https://github.com/pedrolamas)!
### Quick Accent
- Remembered character usage across sessions so frequently used accents appear first. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Added Maltese language support with specific characters and the Euro symbol. Thanks [@rovercoder](https://github.com/rovercoder)!
- Reduced GPU usage issues by making the window Topmost only when the picker is visible. Thanks [@daverayment](https://github.com/daverayment)!
- Added Vietnamese language support to Quick Accent, mappings for Vietnamese vowels (a, e, i, o, u, y) and the letter d. Thanks [@octastylos-pseudodipteros](https://github.com/octastylos-pseudodipteros)!
### Settings
- Added telemetry to track usage of the new shortcut conflict detection workflow.
- Moved the shutdown action from the title bar to a footer menu item with confirmation. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Implemented comprehensive hotkey conflict detection with a dedicated resolution dialog.
- Added branded visuals for Office and Copilot keys in the KeyVisual control.
- Introduced Settings search with fuzzy matching and navigation to specific controls.
- Corrected Spanish localization so product names like Awake remain in English across Settings and OOBE.
- Simplified the Advanced Paste description in Settings for quicker reading and consistent capitalization. Thanks [@OldUser101](https://github.com/OldUser101)!
- Localized conflict messages in the conflict window and dialog.
### Installer
- Upgraded the installer to WiX 5 with silent "Files in Use" handling for smoother winget installs.
- Switched Win10 context menu modules to runtime registration and added cleanup on uninstall to avoid stale entries.
- Completely redesigned the Settings dashboard with a modern card-based layout featuring organized sections for quick actions and shortcuts overview, replacing the old module list.
- Rewrote setting descriptions to be more concise and follow Windows writing style guidelines, making them easier to understand.
- Improved formatting and readability of release notes in the "What's New" section with better typography and spacing.
- Added missing deep link support for various settings pages (Peek, Quick Accent, PowerToys Run, etc.) so you can jump directly to specific settings.
- Resolved an issue where the settings page header would drift away from its position when resizing the settings window.
- Resolved a settings crash related to incompatible property names in ZoomIt configuration.
### Documentation
- Adds docs for building the installer locally and testing winget installs.
- Fixed a broken style guide link in developer documentation. Thanks [@denizmaral](https://github.com/denizmaral)!
- Added detailed step-by-step instructions for first-time developers building the Command Palette module, including prerequisites and Visual Studio setup guidance. Thanks [@chatasweetie](https://github.com/chatasweetie)!
- **Fixed Broken SDK Link**: Corrected a broken markdown link in the Command Palette SDK README that was pointing to an incorrect directory path. Thanks [@ChrisGuzak](https://github.com/ChrisGuzak)!
- Added documentation for the "Open With Cursor" plugin that enables opening Visual Studio and VS Code recent files using Cursor AI. Thanks [@VictorNoxx](https://github.com/VictorNoxx)!
- Added documentation for two new community plugins - Hotkeys plugin for creating custom keyboard shortcuts, and RandomGen plugin for generating random data like passwords, colors, and placeholder text. Thanks [@ruslanlap](https://github.com/ruslanlap)!
### Development
- Excluded test and coverage DLLs from BinSkim scans to cut false positives and speed up security analysis.
- Simplified NOTICE maintenance by removing version numbers and filtering out Microsoft/System packages.
- Improved NuGet dependency validation to prevent package downgrades and catch issues during restore.
- Updated UTF.Unknown to a modern version to improve compatibility without breaking changes. Thanks [@304NotModified](https://github.com/304NotModified)!
- Refreshed package catalog in CI before installing dependencies to prevent Linux workflow failures.
- Refactored CmdPal tests with dependency injection and added coverage for queries and settings.
- Added unit tests to verify Close on Enter swaps Copy/Save as expected. Thanks [@mohammed-saalim](https://github.com/mohammed-saalim)!
- Added accessibility IDs to CmdPal UI for stable UI tests.
- Rewrote system command tests with a new test base and cleaner patterns.
- Added unit tests for WebSearch and Shell extensions with mockable settings.
- Added unit tests and abstractions for Apps and Bookmarks extensions.
- Cleans up AIgenerated tests; adds meaningful query tests across extensions.
- Removed the obsolete debug dialog from Settings for a smoother developer loop.
- Updated .NET libraries to 9.0.8 for performance and security. Thanks [@snickler](https://github.com/snickler)!
- Updated the spell check system to version 0.0.25 with better GitHub integration and SARIF reporting, plus fixed numerous spelling errors throughout the codebase including property names and documentation. Thanks [@jsoref](https://github.com/jsoref)!
- Cleaned up spelling check configuration to eliminate false positives and excessive noise that was appearing in every pull request, making the development process smoother.
- Replaced NuGet feed with Azure Artifacts for better package management.
- Implemented configurable UI test pipeline that can use pre-built official releases instead of building everything from scratch, reducing test execution time from 2+ hours.
- Replaced brittle pixel-by-pixel image comparison with perceptual hash (pHash) technology that's more robust to minor rendering differences - no more test failures due to anti-aliasing or compression artifacts.
- Reduced CI/fuzzing/UI test timeouts from 4 hours to 90 minutes, dramatically improving developer feedback loops and preventing long waits when builds get stuck.
- Standardized test project naming across the entire codebase and improved pipeline result identification by adding platform/install mode context to test run titles. Thanks [@khmyznikov](https://github.com/khmyznikov)!
- Added comprehensive UI test suites for multiple PowerToys modules including Command Palette, Advanced Paste, Peek, Text Extractor, and PowerRename - ensuring better reliability and quality.
- Enhanced UI test automation with command-line argument support, better session management, and improved element location methods using pattern matching to avoid failures from minor differences in exact matches.
### What is being planned over the next few releases
For [v0.95][github-next-release-work], we'll work on the items below:
For [v0.94][github-next-release-work], we'll work on the items below:
- Continued Command Palette polish
- Working on Shortcut Guide v2 (Thanks [@noraa-junker](https://github.com/noraa-junker)!)
- Working on upgrading the installer to WiX 5
- Working on shortcut conflict detection
- Working on setting search
- Upgrading Keyboard Manager's editor UI
- UI tweaking utility with day/night theme switcher
- DSC v3 support for top utilities
- New UI automation tests
- Stability, bug fixes

View File

@@ -76,7 +76,6 @@ Once you've discussed your proposed feature/fix/etc. with a team member, and an
1. Windows 10 April 2018 Update (version 1803) or newer
1. Visual Studio Community/Professional/Enterprise 2022 17.4 or newer
1. A local clone of the PowerToys repository
1. Enable long paths in Windows (see [Enable Long Paths](https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation#enabling-long-paths-in-windows-10-version-1607-and-later) for details)
### Install Visual Studio dependencies

View File

@@ -2,11 +2,11 @@
<configuration>
<packageSources>
<clear />
<add key="PowerToysPublicDependencies" value="https://pkgs.dev.azure.com/shine-oss/PowerToys/_packaging/PowerToysPublicDependencies%40Local/nuget/v3/index.json" />
<add key="PowerToysPublicDependencies" value="https://pkgs.dev.azure.com/shine-oss/PowerToys/_packaging/PowerToysPublicDependencies/nuget/v3/index.json" />
</packageSources>
<packageSourceMapping>
<packageSource key="PowerToysPublicDependencies">
<package pattern="*" />
</packageSource>
</packageSourceMapping>
</configuration>
</configuration>

View File

@@ -17,7 +17,6 @@ namespace Common.UI
Awake,
ColorPicker,
CmdNotFound,
LightSwitch,
FancyZones,
FileLocksmith,
Run,
@@ -61,8 +60,6 @@ namespace Common.UI
return "ColorPicker";
case SettingsWindow.CmdNotFound:
return "CmdNotFound";
case SettingsWindow.LightSwitch:
return "LightSwitch";
case SettingsWindow.FancyZones:
return "FancyZones";
case SettingsWindow.FileLocksmith:

View File

@@ -28,10 +28,6 @@ namespace winrt::PowerToys::GPOWrapper::implementation
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredCropAndLockEnabledValue());
}
GpoRuleConfigured GPOWrapper::GetConfiguredLightSwitchEnabledValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredLightSwitchEnabledValue());
}
GpoRuleConfigured GPOWrapper::GetConfiguredFancyZonesEnabledValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredFancyZonesEnabledValue());

View File

@@ -13,7 +13,6 @@ namespace winrt::PowerToys::GPOWrapper::implementation
static GpoRuleConfigured GetConfiguredCmdPalEnabledValue();
static GpoRuleConfigured GetConfiguredColorPickerEnabledValue();
static GpoRuleConfigured GetConfiguredCropAndLockEnabledValue();
static GpoRuleConfigured GetConfiguredLightSwitchEnabledValue();
static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue();
static GpoRuleConfigured GetConfiguredFileLocksmithEnabledValue();
static GpoRuleConfigured GetConfiguredSvgPreviewEnabledValue();

View File

@@ -17,7 +17,6 @@ namespace PowerToys
static GpoRuleConfigured GetConfiguredCmdPalEnabledValue();
static GpoRuleConfigured GetConfiguredColorPickerEnabledValue();
static GpoRuleConfigured GetConfiguredCropAndLockEnabledValue();
static GpoRuleConfigured GetConfiguredLightSwitchEnabledValue();
static GpoRuleConfigured GetConfiguredFancyZonesEnabledValue();
static GpoRuleConfigured GetConfiguredFileLocksmithEnabledValue();
static GpoRuleConfigured GetConfiguredSvgPreviewEnabledValue();

View File

@@ -122,13 +122,13 @@ namespace ManagedCommon
{
var exMessage =
message + Environment.NewLine +
ex.GetType() + " (" + ex.HResult + "): " + ex.Message + Environment.NewLine;
ex.GetType() + ": " + ex.Message + Environment.NewLine;
if (ex.InnerException != null)
{
exMessage +=
"Inner exception: " + Environment.NewLine +
ex.InnerException.GetType() + " (" + ex.HResult + "): " + ex.InnerException.Message + Environment.NewLine;
ex.InnerException.GetType() + ": " + ex.InnerException.Message + Environment.NewLine;
}
exMessage +=

View File

@@ -12,7 +12,6 @@ namespace ManagedCommon
ColorPicker,
CmdPal,
CropAndLock,
LightSwitch,
EnvironmentVariables,
FancyZones,
FileLocksmith,

View File

@@ -81,7 +81,6 @@ struct LogSettings
inline const static std::string workspacesSnapshotToolLoggerName = "workspaces-snapshot-tool";
inline const static std::wstring workspacesSnapshotToolLogPath = L"workspaces-snapshot-tool-log.log";
inline const static std::string zoomItLoggerName = "zoom-it";
inline const static std::string lightSwitchLoggerName = "light-switch";
inline const static int retention = 30;
std::wstring logLevel;
LogSettings();

View File

@@ -257,10 +257,7 @@ inline HANDLE run_elevated(const std::wstring& file, const std::wstring& params,
exec_info.nShow = SW_HIDE;
}
// failing bc using "runas" with PowerToys.exe already running?
BOOL result = ShellExecuteExW(&exec_info);
return result ? exec_info.hProcess : nullptr;
return ShellExecuteExW(&exec_info) ? exec_info.hProcess : nullptr;
}
// Run command as non-elevated user, returns true if succeeded, puts the process id into returnPid if returnPid != NULL

View File

@@ -30,7 +30,6 @@ namespace powertoys_gpo
const std::wstring POLICY_CONFIGURE_ENABLED_CMD_NOT_FOUND = L"ConfigureEnabledUtilityCmdNotFound";
const std::wstring POLICY_CONFIGURE_ENABLED_COLOR_PICKER = L"ConfigureEnabledUtilityColorPicker";
const std::wstring POLICY_CONFIGURE_ENABLED_CROP_AND_LOCK = L"ConfigureEnabledUtilityCropAndLock";
const std::wstring POLICY_CONFIGURE_ENABLED_LIGHT_SWITCH = L"ConfigureEnabledUtilityLightSwitch";
const std::wstring POLICY_CONFIGURE_ENABLED_FANCYZONES = L"ConfigureEnabledUtilityFancyZones";
const std::wstring POLICY_CONFIGURE_ENABLED_FILE_LOCKSMITH = L"ConfigureEnabledUtilityFileLocksmith";
const std::wstring POLICY_CONFIGURE_ENABLED_SVG_PREVIEW = L"ConfigureEnabledUtilityFileExplorerSVGPreview";
@@ -296,11 +295,6 @@ namespace powertoys_gpo
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_CROP_AND_LOCK);
}
inline gpo_rule_configured_t getConfiguredLightSwitchEnabledValue()
{
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_LIGHT_SWITCH);
}
inline gpo_rule_configured_t getConfiguredFancyZonesEnabledValue()
{
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_FANCYZONES);

View File

@@ -3,7 +3,6 @@
#include <filesystem>
#include <common/version/version.h>
#include <common/SettingsAPI/settings_helpers.h>
#include <common/logger/logger_settings.h>
namespace LoggerHelpers
{

View File

@@ -137,16 +137,6 @@
<decimal value="0" />
</disabledValue>
</policy>
<policy name="ConfigureEnabledUtilityLightSwitch" class="Both" displayName="$(string.ConfigureEnabledUtilityLightSwitch)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityLightSwitch">
<parentCategory ref="PowerToys" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_90_0" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
</policy>
<policy name="ConfigureEnabledUtilityEnvironmentVariables" class="Both" displayName="$(string.ConfigureEnabledUtilityEnvironmentVariables)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityEnvironmentVariables">
<parentCategory ref="PowerToys" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_75_0" />

View File

@@ -245,7 +245,6 @@ If you don't configure this policy, the user will be able to control the setting
<string id="ConfigureEnabledUtilityCmdNotFound">Command Not Found: Configure enabled state</string>
<string id="ConfigureEnabledUtilityCmdPal">CmdPal: Configure enabled state</string>
<string id="ConfigureEnabledUtilityCropAndLock">Crop And Lock: Configure enabled state</string>
<string id="ConfigureEnabledUtilityLightSwitch">Light Switch: Configure enabled state</string>
<string id="ConfigureEnabledUtilityEnvironmentVariables">Environment Variables: Configure enabled state</string>
<string id="ConfigureEnabledUtilityFancyZones">FancyZones: Configure enabled state</string>
<string id="ConfigureEnabledUtilityFileLocksmith">File Locksmith: Configure enabled state</string>

View File

@@ -20,14 +20,27 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TitleBar x:Name="titleBar">
<!-- This is a workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/10374, once fixed we should just be using IconSource -->
<TitleBar.LeftHeader>
<ImageIcon
Height="16"
Margin="16,0,0,0"
Source="/Assets/EnvironmentVariables/EnvironmentVariables.ico" />
</TitleBar.LeftHeader>
</TitleBar>
<Grid
x:Name="titleBar"
Height="32"
ColumnSpacing="16">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="LeftPaddingColumn" Width="0" />
<ColumnDefinition x:Name="IconColumn" Width="Auto" />
<ColumnDefinition x:Name="TitleColumn" Width="Auto" />
<ColumnDefinition x:Name="RightPaddingColumn" Width="0" />
</Grid.ColumnDefinitions>
<Image
Grid.Column="1"
Width="16"
Height="16"
VerticalAlignment="Center"
Source="../Assets/EnvironmentVariables/EnvironmentVariables.ico" />
<TextBlock
x:Name="AppTitleTextBlock"
Grid.Column="2"
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}" />
</Grid>
</Grid>
</winuiex:WindowEx>

View File

@@ -4,19 +4,22 @@
using System;
using System.Runtime.InteropServices;
using EnvironmentVariables.Win32;
using EnvironmentVariablesUILib;
using EnvironmentVariablesUILib.Helpers;
using EnvironmentVariablesUILib.ViewModels;
using ManagedCommon;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using WinUIEx;
namespace EnvironmentVariables
{
/// <summary>
/// An empty window that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainWindow : WindowEx
{
private EnvironmentVariablesMainPage MainPage { get; }
@@ -31,9 +34,8 @@ namespace EnvironmentVariables
AppWindow.SetIcon("Assets/EnvironmentVariables/EnvironmentVariables.ico");
var loader = ResourceLoaderInstance.ResourceLoader;
var title = App.GetService<IElevationHelper>().IsElevated ? loader.GetString("WindowAdminTitle") : loader.GetString("WindowTitle");
Title = title;
titleBar.Title = title;
AppTitleTextBlock.Text = title;
var handle = this.GetWindowHandle();
RegisterWindow(handle);

View File

@@ -20,15 +20,30 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TitleBar x:Name="titleBar">
<!-- This is a workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/10374, once fixed we should just be using IconSource -->
<TitleBar.LeftHeader>
<ImageIcon
Height="16"
Margin="16,0,0,0"
Source="/Assets/FileLocksmith/Icon.ico" />
</TitleBar.LeftHeader>
</TitleBar>
<Grid
x:Name="AppTitleBar"
Height="32"
ColumnSpacing="16">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="LeftPaddingColumn" Width="0" />
<ColumnDefinition x:Name="IconColumn" Width="Auto" />
<ColumnDefinition x:Name="TitleColumn" Width="Auto" />
<ColumnDefinition x:Name="RightDragColumn" Width="*" />
<ColumnDefinition x:Name="RightPaddingColumn" Width="0" />
</Grid.ColumnDefinitions>
<Image
Grid.Column="1"
Width="16"
Height="16"
VerticalAlignment="Center"
Source="../Assets/FileLocksmith/Icon.ico" />
<TextBlock
x:Name="AppTitleTextBlock"
Grid.Column="2"
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}" />
</Grid>
<views:MainPage x:Name="mainPage" Grid.Row="1" />
</Grid>
</winuiex:WindowEx>
</winuiex:WindowEx>

View File

@@ -18,16 +18,30 @@ namespace FileLocksmithUI
{
InitializeComponent();
mainPage.ViewModel.IsElevated = isElevated;
SetTitleBar(titleBar);
ExtendsContentIntoTitleBar = true;
AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Tall;
SetTitleBar(AppTitleBar);
Activated += MainWindow_Activated;
AppWindow.SetIcon("Assets/FileLocksmith/Icon.ico");
WindowHelpers.ForceTopBorder1PixelInsetOnWindows10(this.GetWindowHandle());
var loader = ResourceLoaderInstance.ResourceLoader;
var title = isElevated ? loader.GetString("AppAdminTitle") : loader.GetString("AppTitle");
Title = title;
titleBar.Title = title;
AppTitleTextBlock.Text = title;
}
private void MainWindow_Activated(object sender, WindowActivatedEventArgs args)
{
if (args.WindowActivationState == WindowActivationState.Deactivated)
{
AppTitleTextBlock.Foreground =
(SolidColorBrush)App.Current.Resources["WindowCaptionForegroundDisabled"];
}
else
{
AppTitleTextBlock.Foreground =
(SolidColorBrush)App.Current.Resources["WindowCaptionForeground"];
}
}
public void Dispose()

View File

@@ -190,7 +190,7 @@
TextWrapping="Wrap" />
</ContentDialog>
<ContentDialog x:Name="ProcessFilesListDialog" x:Uid="ProcessFilesListDialog">
<ScrollViewer Padding="16" HorizontalScrollBarVisibility="Auto">
<ScrollViewer Padding="15" HorizontalScrollBarVisibility="Auto">
<TextBlock
x:Name="ProcessFilesListDialogTextBlock"
x:Uid="ProcessFilesListDialogTextBlock"

View File

@@ -20,14 +20,27 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TitleBar x:Name="titleBar">
<!-- This is a workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/10374, once fixed we should just be using IconSource -->
<TitleBar.LeftHeader>
<ImageIcon
Height="16"
Margin="16,0,0,0"
Source="/Assets/Hosts/Hosts.ico" />
</TitleBar.LeftHeader>
</TitleBar>
<Grid
x:Name="titleBar"
Height="32"
ColumnSpacing="16">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="LeftPaddingColumn" Width="0" />
<ColumnDefinition x:Name="IconColumn" Width="Auto" />
<ColumnDefinition x:Name="TitleColumn" Width="Auto" />
<ColumnDefinition x:Name="RightPaddingColumn" Width="0" />
</Grid.ColumnDefinitions>
<Image
Grid.Column="1"
Width="16"
Height="16"
VerticalAlignment="Center"
Source="../Assets/Hosts/Hosts.ico" />
<TextBlock
x:Name="AppTitleTextBlock"
Grid.Column="2"
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}" />
</Grid>
</Grid>
</winuiex:WindowEx>

View File

@@ -9,15 +9,19 @@ using HostsUILib.Helpers;
using HostsUILib.Views;
using ManagedCommon;
using Microsoft.PowerToys.Telemetry;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Microsoft.Windows.ApplicationModel.Resources;
using WinUIEx;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace Hosts
{
/// <summary>
/// An empty window that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainWindow : WindowEx
{
private HostsMainPage MainPage { get; }
@@ -34,18 +38,31 @@ namespace Hosts
var title = Host.GetService<IElevationHelper>().IsElevated ? loader.GetString("WindowAdminTitle") : loader.GetString("WindowTitle");
Title = title;
titleBar.Title = title;
AppTitleTextBlock.Text = title;
var handle = this.GetWindowHandle();
WindowHelpers.ForceTopBorder1PixelInsetOnWindows10(handle);
WindowHelpers.BringToForeground(handle);
Activated += MainWindow_Activated;
MainPage = Host.GetService<HostsMainPage>();
PowerToysTelemetry.Log.WriteEvent(new HostEditorStartFinishEvent() { TimeStamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() });
}
private void MainWindow_Activated(object sender, WindowActivatedEventArgs args)
{
if (args.WindowActivationState == WindowActivationState.Deactivated)
{
AppTitleTextBlock.Foreground = (SolidColorBrush)App.Current.Resources["WindowCaptionForegroundDisabled"];
}
else
{
AppTitleTextBlock.Foreground = (SolidColorBrush)App.Current.Resources["WindowCaptionForeground"];
}
}
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
MainGrid.Children.Add(MainPage);

View File

@@ -1,32 +0,0 @@
1 VERSIONINFO
FILEVERSION 0,1,0,0
PRODUCTVERSION 0,1,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "Company Name"
VALUE "FileDescription", "Light Switch Module"
VALUE "FileVersion", "0.1.0.0"
VALUE "InternalName", "Light Switch"
VALUE "LegalCopyright", "Copyright (C) 2019 Company Name"
VALUE "OriginalFilename", "PowerToys.LightSwitchModuleInterface.dll"
VALUE "ProductName", "Light Switch"
VALUE "ProductVersion", "0.1.0.0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

View File

@@ -1,25 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36127.28 d17.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LightSwitchModuleInterface", "LightSwitchModuleInterface.vcxproj", "{38177D56-6AD1-4ADF-88C9-2843A7932166}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{38177D56-6AD1-4ADF-88C9-2843A7932166}.Debug|x64.ActiveCfg = Debug|x64
{38177D56-6AD1-4ADF-88C9-2843A7932166}.Debug|x64.Build.0 = Debug|x64
{38177D56-6AD1-4ADF-88C9-2843A7932166}.Release|x64.ActiveCfg = Release|x64
{38177D56-6AD1-4ADF-88C9-2843A7932166}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FAF634A3-0D98-4A45-B082-D93B59782572}
EndGlobalSection
EndGlobal

View File

@@ -1,229 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{38177d56-6ad1-4adf-88c9-2843a7932166}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>LightSwitchModuleInterface</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>LightSwitchModuleInterface</ProjectName>
<TargetName>PowerToys.LightSwitchModuleInterface</TargetName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<LanguageStandard>stdcpplatest</LanguageStandard>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;EXAMPLEPOWERTOY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>
$(SolutionDir)src\;
$(SolutionDir)src\modules;
$(SolutionDir)src\common\Telemetry;
%(AdditionalIncludeDirectories)
</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<AdditionalDependencies Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">$(CoreLibraryDependencies);%(AdditionalDependencies);advapi32.lib</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="ThemeHelper.h" />
<ClInclude Include="trace.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">Create</PrecompiledHeader>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|x64'">pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">pch.h</PrecompiledHeaderFile>
<PrecompiledHeaderFile Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">pch.h</PrecompiledHeaderFile>
</ClCompile>
<ClCompile Include="ThemeHelper.cpp" />
<ClCompile Include="trace.cpp" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="LightSwitchModuleInterface.rc" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj">
<Project>{4aed67b6-55fd-486f-b917-e543dee2cb3c}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="..\..\..\..\deps\spdlog.props" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
</Target>
</Project>

View File

@@ -1,50 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="dllmain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="trace.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ThemeHelper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="trace.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ThemeHelper.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Header Files">
<UniqueIdentifier>{bbf22ac8-46f8-4206-b44b-9c3897e99ce5}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files">
<UniqueIdentifier>{530ed784-9a70-46a0-8fb6-20d5dee4f7d3}</UniqueIdentifier>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{da1cb871-86d3-414c-adf5-a7e9f2077d2f}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="LightSwitchModuleInterface.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
</Project>

View File

@@ -1,81 +0,0 @@
#include "pch.h"
#include <windows.h>
#include "ThemeHelper.h"
// Controls changing the themes.
void SetAppsTheme(bool mode)
{
HKEY hKey;
if (RegOpenKeyEx(HKEY_CURRENT_USER,
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
0,
KEY_SET_VALUE,
&hKey) == ERROR_SUCCESS)
{
DWORD value = mode;
RegSetValueEx(hKey, L"AppsUseLightTheme", 0, REG_DWORD, reinterpret_cast<const BYTE*>(&value), sizeof(value));
RegCloseKey(hKey);
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, reinterpret_cast<LPARAM>(L"ImmersiveColorSet"), SMTO_ABORTIFHUNG, 5000, nullptr);
SendMessageTimeout(HWND_BROADCAST, WM_THEMECHANGED, 0, 0, SMTO_ABORTIFHUNG, 5000, nullptr);
}
}
void SetSystemTheme(bool mode)
{
HKEY hKey;
if (RegOpenKeyEx(HKEY_CURRENT_USER,
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
0,
KEY_SET_VALUE,
&hKey) == ERROR_SUCCESS)
{
DWORD value = mode;
RegSetValueEx(hKey, L"SystemUsesLightTheme", 0, REG_DWORD, reinterpret_cast<const BYTE*>(&value), sizeof(value));
RegCloseKey(hKey);
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, reinterpret_cast<LPARAM>(L"ImmersiveColorSet"), SMTO_ABORTIFHUNG, 5000, nullptr);
SendMessageTimeout(HWND_BROADCAST, WM_THEMECHANGED, 0, 0, SMTO_ABORTIFHUNG, 5000, nullptr);
}
}
bool GetCurrentSystemTheme()
{
HKEY hKey;
DWORD value = 1; // default = light
DWORD size = sizeof(value);
if (RegOpenKeyEx(HKEY_CURRENT_USER,
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
0,
KEY_READ,
&hKey) == ERROR_SUCCESS)
{
RegQueryValueEx(hKey, L"SystemUsesLightTheme", nullptr, nullptr, reinterpret_cast<LPBYTE>(&value), &size);
RegCloseKey(hKey);
}
return value == 1; // true = light, false = dark
}
bool GetCurrentAppsTheme()
{
HKEY hKey;
DWORD value = 1;
DWORD size = sizeof(value);
if (RegOpenKeyEx(HKEY_CURRENT_USER,
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
0,
KEY_READ,
&hKey) == ERROR_SUCCESS)
{
RegQueryValueEx(hKey, L"AppsUseLightTheme", nullptr, nullptr, reinterpret_cast<LPBYTE>(&value), &size);
RegCloseKey(hKey);
}
return value == 1; // true = light, false = dark
}

View File

@@ -1,5 +0,0 @@
#pragma once
void SetSystemTheme(bool dark);
void SetAppsTheme(bool dark);
bool GetCurrentSystemTheme();
bool GetCurrentAppsTheme();

View File

@@ -1,546 +0,0 @@
#include "pch.h"
#include <interface/powertoy_module_interface.h>
#include "trace.h"
#include <common/logger/logger.h>
#include <common/SettingsAPI/settings_objects.h>
#include <common/SettingsAPI/settings_helpers.h>
#include <locale>
#include <codecvt>
#include <common/utils/logger_helper.h>
#include "ThemeHelper.h"
extern "C" IMAGE_DOS_HEADER __ImageBase;
namespace
{
const wchar_t JSON_KEY_PROPERTIES[] = L"properties";
const wchar_t JSON_KEY_WIN[] = L"win";
const wchar_t JSON_KEY_ALT[] = L"alt";
const wchar_t JSON_KEY_CTRL[] = L"ctrl";
const wchar_t JSON_KEY_SHIFT[] = L"shift";
const wchar_t JSON_KEY_CODE[] = L"code";
const wchar_t JSON_KEY_TOGGLE_THEME_HOTKEY[] = L"toggle-theme-hotkey";
const wchar_t JSON_KEY_VALUE[] = L"value";
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
Trace::RegisterProvider();
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
Trace::UnregisterProvider();
break;
}
return TRUE;
}
// The PowerToy name that will be shown in the settings.
const static wchar_t* MODULE_NAME = L"LightSwitch";
// Add a description that will we shown in the module settings page.
const static wchar_t* MODULE_DESC = L"This is a module that allows you to control light/dark theming via set times, sun rise, or directly invoking the change.";
enum class ScheduleMode
{
FixedHours,
SunsetToSunriseGeo,
SunsetToSunriseUser
// add more later
};
inline std::wstring ToString(ScheduleMode mode)
{
switch (mode)
{
case ScheduleMode::SunsetToSunriseGeo:
return L"SunsetToSunriseGeo";
case ScheduleMode::SunsetToSunriseUser:
return L"SunsetToSunriseUser";
case ScheduleMode::FixedHours:
default:
return L"FixedHours";
}
}
inline ScheduleMode FromString(const std::wstring& str)
{
if (str == L"SunsetToSunriseGeo")
return ScheduleMode::SunsetToSunriseGeo;
if (str == L"SunsetToSunriseUser")
return ScheduleMode::SunsetToSunriseUser;
return ScheduleMode::FixedHours;
}
// These are the properties shown in the Settings page.
struct ModuleSettings
{
bool m_changeSystem = true;
bool m_changeApps = true;
ScheduleMode m_scheduleMode = ScheduleMode::FixedHours;
int m_lightTime = 480;
int m_darkTime = 1200;
int m_offset = 0;
std::wstring m_latitude = L"0.0";
std::wstring m_longitude = L"0.0";
} g_settings;
class LightSwitchInterface : public PowertoyModuleIface
{
private:
bool m_enabled = false;
HANDLE m_process{ nullptr };
HANDLE m_force_light_event_handle;
HANDLE m_force_dark_event_handle;
static const constexpr int NUM_DEFAULT_HOTKEYS = 4;
Hotkey m_toggle_theme_hotkey = { .win = true, .ctrl = true, .shift = true, .alt = false, .key = 'D' };
void init_settings();
public:
LightSwitchInterface()
{
LoggerHelpers::init_logger(L"LightSwitch", L"ModuleInterface", LogSettings::lightSwitchLoggerName);
m_force_light_event_handle = CreateDefaultEvent(L"POWEROYS_LIGHTSWITCH_FORCE_LIGHT");
m_force_dark_event_handle = CreateDefaultEvent(L"POWEROYS_LIGHTSWITCH_FORCE_DARK");
init_settings();
};
virtual const wchar_t* get_key() override
{
return L"LightSwitch";
}
// Destroy the powertoy and free memory
virtual void destroy() override
{
delete this;
}
// Return the display name of the powertoy, this will be cached by the runner
virtual const wchar_t* get_name() override
{
return MODULE_NAME;
}
// Return the configured status for the gpo policy for the module
virtual powertoys_gpo::gpo_rule_configured_t gpo_policy_enabled_configuration() override
{
return powertoys_gpo::getConfiguredLightSwitchEnabledValue();
}
// Return JSON with the configuration options.
virtual bool get_config(wchar_t* buffer, int* buffer_size) override
{
HINSTANCE hinstance = reinterpret_cast<HINSTANCE>(&__ImageBase);
// Create a Settings object with your module name
PowerToysSettings::Settings settings(hinstance, get_name());
settings.set_description(MODULE_DESC);
settings.set_overview_link(L"https://aka.ms/powertoys");
// Boolean toggles
settings.add_bool_toggle(
L"changeSystem",
L"Change System Theme",
g_settings.m_changeSystem);
settings.add_bool_toggle(
L"changeApps",
L"Change Apps Theme",
g_settings.m_changeApps);
settings.add_choice_group(
L"scheduleMode",
L"Theme schedule mode",
ToString(g_settings.m_scheduleMode),
{ { L"FixedHours", L"Set hours manually" },
{ L"SunsetToSunriseGeo", L"Use sunrise/sunset times (Geolocation)" },
{ L"SunsetToSunriseUser", L"Use sunrise/sunset times (User selected)" } });
// Integer spinners
settings.add_int_spinner(
L"lightTime",
L"Time to switch to light theme (minutes after midnight).",
g_settings.m_lightTime,
0,
1439,
1);
settings.add_int_spinner(
L"darkTime",
L"Time to switch to dark theme (minutes after midnight).",
g_settings.m_darkTime,
0,
1439,
1);
settings.add_int_spinner(
L"offset",
L"Time to offset turning on your light/dark themes.",
g_settings.m_offset,
0,
1439,
1);
// Strings for latitude and longitude
settings.add_string(
L"latitude",
L"Your latitude in decimal degrees (e.g. 39.95).",
g_settings.m_latitude);
settings.add_string(
L"longitude",
L"Your longitude in decimal degrees (e.g. -75.16).",
g_settings.m_longitude);
// One-shot actions (buttons)
settings.add_custom_action(
L"forceLight",
L"Switch immediately to light theme",
L"Force Light",
L"{}");
settings.add_custom_action(
L"forceDark",
L"Switch immediately to dark theme",
L"Force Dark",
L"{}");
// Hotkeys
PowerToysSettings::HotkeyObject dm_hk = PowerToysSettings::HotkeyObject::from_settings(
m_toggle_theme_hotkey.win,
m_toggle_theme_hotkey.ctrl,
m_toggle_theme_hotkey.alt,
m_toggle_theme_hotkey.shift,
m_toggle_theme_hotkey.key);
settings.add_hotkey(
L"toggle-theme-hotkey",
L"Shortcut to toggle theme immediately",
dm_hk);
// Serialize to buffer for the PowerToys runner
return settings.serialize_to_buffer(buffer, buffer_size);
}
// Signal from the Settings editor to call a custom action.
// This can be used to spawn more complex editors.
void call_custom_action(const wchar_t* action) override
{
try
{
auto action_object = PowerToysSettings::CustomActionObject::from_json_string(action);
if (action_object.get_name() == L"forceLight")
{
Logger::info(L"[Light Switch] Custom action triggered: Force Light");
SetSystemTheme(true);
SetAppsTheme(true);
}
else if (action_object.get_name() == L"forceDark")
{
Logger::info(L"[Light Switch] Custom action triggered: Force Dark");
SetSystemTheme(false);
SetAppsTheme(false);
}
}
catch (...)
{
Logger::error(L"[Light Switch] Invalid custom action JSON");
}
}
// Called by the runner to pass the updated settings values as a serialized JSON.
virtual void set_config(const wchar_t* config) override
{
try
{
auto values = PowerToysSettings::PowerToyValues::from_json_string(config, get_key());
parse_hotkey(values);
if (auto v = values.get_bool_value(L"changeSystem"))
{
g_settings.m_changeSystem = *v;
}
if (auto v = values.get_bool_value(L"changeApps"))
{
g_settings.m_changeApps = *v;
}
if (auto v = values.get_string_value(L"scheduleMode"))
{
g_settings.m_scheduleMode = FromString(*v);
}
if (auto v = values.get_int_value(L"lightTime"))
{
g_settings.m_lightTime = *v;
}
if (auto v = values.get_int_value(L"darkTime"))
{
g_settings.m_darkTime = *v;
}
if (auto v = values.get_int_value(L"offset"))
{
g_settings.m_offset = *v;
}
if (auto v = values.get_string_value(L"latitude"))
{
g_settings.m_latitude = *v;
}
if (auto v = values.get_string_value(L"longitude"))
{
g_settings.m_longitude = *v;
}
values.save_to_settings_file();
}
catch (const std::exception&)
{
Logger::error("[Light Switch] set_config: Failed to parse or apply config.");
}
}
virtual void enable()
{
m_enabled = true;
Logger::info(L"Enabling Light Switch module...");
unsigned long powertoys_pid = GetCurrentProcessId();
std::wstring args = L"--pid " + std::to_wstring(powertoys_pid);
std::wstring exe_name = L"LightSwitchService\\PowerToys.LightSwitchService.exe";
std::wstring resolved_path(MAX_PATH, L'\0');
DWORD result = SearchPathW(
nullptr,
exe_name.c_str(),
nullptr,
static_cast<DWORD>(resolved_path.size()),
resolved_path.data(),
nullptr);
if (result == 0 || result >= resolved_path.size())
{
Logger::error(L"Failed to locate Light Switch executable: '{}'", exe_name);
return;
}
resolved_path.resize(result);
Logger::debug(L"Resolved executable path: {}", resolved_path);
std::wstring command_line = L"\"" + resolved_path + L"\" " + args;
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
if (!CreateProcessW(
resolved_path.c_str(),
command_line.data(),
nullptr,
nullptr,
TRUE,
0,
nullptr,
nullptr,
&si,
&pi))
{
Logger::error(L"Failed to launch Light Switch process. {}", get_last_error_or_default(GetLastError()));
return;
}
Logger::info(L"Light Switch process launched successfully (PID: {}).", pi.dwProcessId);
m_process = pi.hProcess;
CloseHandle(pi.hThread);
}
// Disable the powertoy
virtual void disable()
{
Logger::info("Light Switch disabling");
m_enabled = false;
if (m_process)
{
constexpr DWORD timeout_ms = 1500;
DWORD result = WaitForSingleObject(m_process, timeout_ms);
if (result == WAIT_TIMEOUT)
{
Logger::warn("Light Switch: Process didn't exit in time. Forcing termination.");
TerminateProcess(m_process, 0);
}
CloseHandle(m_process);
m_process = nullptr;
}
}
// Returns if the powertoys is enabled
virtual bool is_enabled() override
{
return m_enabled;
}
void parse_hotkey(PowerToysSettings::PowerToyValues& settings)
{
auto settingsObject = settings.get_raw_json();
if (settingsObject.GetView().Size())
{
try
{
Hotkey _temp_toggle_theme;
auto jsonHotkeyObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_TOGGLE_THEME_HOTKEY).GetNamedObject(JSON_KEY_VALUE);
_temp_toggle_theme.win = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_WIN);
_temp_toggle_theme.alt = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_ALT);
_temp_toggle_theme.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT);
_temp_toggle_theme.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL);
_temp_toggle_theme.key = static_cast<unsigned char>(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE));
m_toggle_theme_hotkey = _temp_toggle_theme;
}
catch (...)
{
Logger::error("Failed to initialize Light Switch force dark mode shortcut from settings. Value will keep unchanged.");
}
}
else
{
Logger::info("Light Switch settings are empty");
}
}
virtual size_t get_hotkeys(Hotkey* hotkeys, size_t buffer_size) override
{
if (hotkeys && buffer_size >= 1)
{
hotkeys[0] = m_toggle_theme_hotkey;
}
return 1;
}
virtual bool on_hotkey(size_t hotkeyId) override
{
if (m_enabled)
{
Logger::trace(L"Light Switch hotkey pressed");
if (!is_process_running())
{
enable();
}
else if (hotkeyId == 0)
{
// get current will return true if in light mode, otherwise false
Logger::info(L"[Light Switch] Hotkey triggered: Toggle Theme");
if (g_settings.m_changeSystem)
{
SetSystemTheme(!GetCurrentSystemTheme());
}
if (g_settings.m_changeApps)
{
SetAppsTheme(!GetCurrentAppsTheme());
}
}
return true;
}
return false;
}
bool is_process_running()
{
return WaitForSingleObject(m_process, 0) == WAIT_TIMEOUT;
}
};
std::wstring utf8_to_wstring(const std::string& str)
{
if (str.empty())
return std::wstring();
int size_needed = MultiByteToWideChar(
CP_UTF8,
0,
str.c_str(),
static_cast<int>(str.size()),
nullptr,
0);
std::wstring wstr(size_needed, 0);
MultiByteToWideChar(
CP_UTF8,
0,
str.c_str(),
static_cast<int>(str.size()),
&wstr[0],
size_needed);
return wstr;
}
// Load the settings file.
void LightSwitchInterface::init_settings()
{
Logger::info(L"[Light Switch] init_settings: starting to load settings for module");
try
{
PowerToysSettings::PowerToyValues settings =
PowerToysSettings::PowerToyValues::load_from_settings_file(get_name());
parse_hotkey(settings);
if (auto v = settings.get_bool_value(L"changeSystem"))
g_settings.m_changeSystem = *v;
if (auto v = settings.get_bool_value(L"changeApps"))
g_settings.m_changeApps = *v;
if (auto v = settings.get_string_value(L"scheduleMode"))
g_settings.m_scheduleMode = FromString(*v);
if (auto v = settings.get_int_value(L"lightTime"))
g_settings.m_lightTime = *v;
if (auto v = settings.get_int_value(L"darkTime"))
g_settings.m_darkTime = *v;
if (auto v = settings.get_int_value(L"offset"))
g_settings.m_offset = *v;
if (auto v = settings.get_string_value(L"latitude"))
g_settings.m_latitude = *v;
if (auto v = settings.get_string_value(L"longitude"))
g_settings.m_longitude = *v;
Logger::info(L"[Light Switch] init_settings: loaded successfully");
}
catch (const winrt::hresult_error& e)
{
Logger::error(L"[Light Switch] init_settings: hresult_error 0x{:08X} - {}", e.code(), e.message().c_str());
}
catch (const std::exception& e)
{
std::wstring whatStr = utf8_to_wstring(e.what());
Logger::error(L"[Light Switch] init_settings: std::exception - {}", whatStr);
}
catch (...)
{
Logger::error(L"[Light Switch] init_settings: unknown exception while loading settings");
}
}
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
{
return new LightSwitchInterface();
}

View File

@@ -1,2 +0,0 @@
#include "pch.h"
#pragma comment(lib, "windowsapp")

View File

@@ -1,14 +0,0 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <common/SettingsAPI/settings_helpers.h>
#include <common/utils/gpo.h>
#include <common/utils/winapi_error.h>
#include <shlwapi.h>
#include <shellapi.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.System.h>
#include <winrt/Windows.Globalization.h>
#include <winrt/Windows.ApplicationModel.h>
#include <winrt/Windows.ApplicationModel.Core.h>

View File

@@ -1,30 +0,0 @@
#include "pch.h"
#include "trace.h"
#include <TraceLoggingProvider.h>
TRACELOGGING_DEFINE_PROVIDER(
g_hProvider,
"Microsoft.PowerToys",
// {38e8889b-9731-53f5-e901-e8a7c1753074}
(0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
TraceLoggingOptionProjectTelemetry());
void Trace::RegisterProvider()
{
TraceLoggingRegister(g_hProvider);
}
void Trace::UnregisterProvider()
{
TraceLoggingUnregister(g_hProvider);
}
void Trace::MyEvent()
{
TraceLoggingWrite(
g_hProvider,
"PowerToyName_MyEvent",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}

View File

@@ -1,15 +0,0 @@
#pragma once
#include <windows.h>
#include <TraceLoggingActivity.h>
#include <common/telemetry/ProjectTelemetry.h>
TRACELOGGING_DECLARE_PROVIDER(g_hProvider);
class Trace
{
public:
static void RegisterProvider();
static void UnregisterProvider();
static void MyEvent();
};

View File

@@ -1,251 +0,0 @@
#include <windows.h>
#include <tchar.h>
#include "ThemeScheduler.h"
#include "ThemeHelper.h"
#include <stdio.h>
#include <string>
#include <LightSwitchSettings.h>
#include <common/utils/gpo.h>
SERVICE_STATUS g_ServiceStatus = {};
SERVICE_STATUS_HANDLE g_StatusHandle = nullptr;
HANDLE g_ServiceStopEvent = nullptr;
VOID WINAPI ServiceMain(DWORD argc, LPTSTR* argv);
VOID WINAPI ServiceCtrlHandler(DWORD dwCtrl);
DWORD WINAPI ServiceWorkerThread(LPVOID lpParam);
// Entry point for the executable
int _tmain(int argc, TCHAR* argv[])
{
DWORD parentPid = 0;
bool debug = false;
for (int i = 1; i < argc; ++i)
{
if (_tcscmp(argv[i], _T("--debug")) == 0)
debug = true;
else if (_tcscmp(argv[i], _T("--pid")) == 0 && i + 1 < argc)
parentPid = _tstoi(argv[++i]);
}
if (debug)
{
// Create a console window for debug output
AllocConsole();
FILE* f;
freopen_s(&f, "CONOUT$", "w", stdout);
freopen_s(&f, "CONOUT$", "w", stderr);
SetConsoleTitle(L"LightSwitchService Debug");
// Console mode (debug)
g_ServiceStopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
ServiceWorkerThread(reinterpret_cast<void*>(static_cast<ULONG_PTR>(parentPid)));
CloseHandle(g_ServiceStopEvent);
FreeConsole();
return 0;
}
// Try to connect to SCM
wchar_t serviceName[] = L"LightSwitchService";
SERVICE_TABLE_ENTRYW table[] = { { serviceName, ServiceMain }, { nullptr, nullptr } };
if (!StartServiceCtrlDispatcherW(table))
{
DWORD err = GetLastError();
if (err == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) // not launched by SCM
{
g_ServiceStopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
HANDLE hThread = CreateThread(
nullptr, 0, ServiceWorkerThread, reinterpret_cast<void*>(static_cast<ULONG_PTR>(parentPid)), 0, nullptr);
// Wait so the process stays alive
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(g_ServiceStopEvent);
return 0;
}
return static_cast<int>(err);
}
return 0;
}
// Called when the service is launched by Windows
VOID WINAPI ServiceMain(DWORD, LPTSTR*)
{
g_StatusHandle = RegisterServiceCtrlHandler(_T("LightSwitchService"), ServiceCtrlHandler);
if (!g_StatusHandle)
return;
g_ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
SetServiceStatus(g_StatusHandle, &g_ServiceStatus);
g_ServiceStopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
if (!g_ServiceStopEvent)
{
g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
g_ServiceStatus.dwWin32ExitCode = GetLastError();
SetServiceStatus(g_StatusHandle, &g_ServiceStatus);
return;
}
SECURITY_ATTRIBUTES sa{ sizeof(sa) };
sa.bInheritHandle = FALSE;
sa.lpSecurityDescriptor = nullptr;
g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(g_StatusHandle, &g_ServiceStatus);
HANDLE hThread = CreateThread(nullptr, 0, ServiceWorkerThread, nullptr, 0, nullptr);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(g_ServiceStopEvent);
g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
g_ServiceStatus.dwWin32ExitCode = 0;
SetServiceStatus(g_StatusHandle, &g_ServiceStatus);
}
VOID WINAPI ServiceCtrlHandler(DWORD dwCtrl)
{
switch (dwCtrl)
{
case SERVICE_CONTROL_STOP:
if (g_ServiceStatus.dwCurrentState != SERVICE_RUNNING)
break;
g_ServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus(g_StatusHandle, &g_ServiceStatus);
// Signal the service to stop
SetEvent(g_ServiceStopEvent);
break;
default:
break;
}
}
DWORD WINAPI ServiceWorkerThread(LPVOID lpParam)
{
DWORD parentPid = static_cast<DWORD>(reinterpret_cast<ULONG_PTR>(lpParam));
HANDLE hParent = nullptr;
if (parentPid)
hParent = OpenProcess(SYNCHRONIZE, FALSE, parentPid);
OutputDebugString(L"[LightSwitchService] Worker thread starting...\n");
// Initialize settings system
LightSwitchSettings::instance().InitFileWatcher();
auto applyTheme = [](int nowMinutes, int lightMinutes, int darkMinutes, const auto& settings) {
bool isLightActive = false;
if (lightMinutes < darkMinutes)
{
// Normal case: sunrise < sunset
isLightActive = (nowMinutes >= lightMinutes && nowMinutes < darkMinutes);
}
else
{
// Wraparound case: e.g. light at 21:00, dark at 06:00
isLightActive = (nowMinutes >= lightMinutes || nowMinutes < darkMinutes);
}
if (isLightActive)
{
if (settings.changeSystem)
SetSystemTheme(true);
if (settings.changeApps)
SetAppsTheme(true);
}
else
{
if (settings.changeSystem)
SetSystemTheme(false);
if (settings.changeApps)
SetAppsTheme(false);
}
};
// --- At service start: immediately honor the schedule ---
{
SYSTEMTIME st;
GetLocalTime(&st);
int nowMinutes = st.wHour * 60 + st.wMinute;
LightSwitchSettings::instance().LoadSettings();
const auto& settings = LightSwitchSettings::instance().settings();
applyTheme(nowMinutes, settings.lightTime + settings.offset, settings.darkTime + settings.offset, settings);
}
// --- Main loop: wakes once per minute or stop/parent death ---
for (;;)
{
HANDLE waits[2] = { g_ServiceStopEvent, hParent };
DWORD count = hParent ? 2 : 1;
SYSTEMTIME st;
GetLocalTime(&st);
int nowMinutes = st.wHour * 60 + st.wMinute;
LightSwitchSettings::instance().LoadSettings();
const auto& settings = LightSwitchSettings::instance().settings();
// Debug print
wchar_t msg[160];
swprintf_s(msg,
L"[LightSwitchService] now=%02d:%02d | light=%02d:%02d | dark=%02d:%02d\n",
st.wHour,
st.wMinute,
settings.lightTime / 60,
settings.lightTime % 60,
settings.darkTime / 60,
settings.darkTime % 60);
OutputDebugString(msg);
// Apply theme logic
applyTheme(nowMinutes, settings.lightTime + settings.offset, settings.darkTime + settings.offset, settings);
// Sleep until next minute, wake early if stop/parent dies
GetLocalTime(&st);
int msToNextMinute = (60 - st.wSecond) * 1000 - st.wMilliseconds;
if (msToNextMinute < 50)
msToNextMinute = 50;
DWORD wait = WaitForMultipleObjects(count, waits, FALSE, msToNextMinute);
if (wait == WAIT_OBJECT_0) // stop event
break;
if (hParent && wait == WAIT_OBJECT_0 + 1) // parent exited
break;
}
if (hParent)
CloseHandle(hParent);
return 0;
}
int APIENTRY wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
if (powertoys_gpo::getConfiguredLightSwitchEnabledValue() == powertoys_gpo::gpo_rule_configured_disabled)
{
wchar_t msg[160];
swprintf_s(
msg,
L"Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator.\n");
OutputDebugString(msg);
return 0;
}
int argc = 0;
LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc);
int rc = _tmain(argc, argv); // reuse your existing logic
LocalFree(argv);
return rc;
}

View File

@@ -1,254 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{08e71c67-6a7e-4ca1-b04e-2fb336410bac}</ProjectGuid>
<RootNamespace>LightSwitchService</RootNamespace>
<WindowsTargetPlatformVersion>10.0.26100.0</WindowsTargetPlatformVersion>
<ProjectName>LightSwitchService</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\$(MSBuildProjectName)\</OutDir>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\$(MSBuildProjectName)\</IntDir>
<TargetName>PowerToys.LightSwitchService</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<AdditionalIncludeDirectories>
./../;
$(SolutionDir)src\common\Telemetry;
$(SolutionDir)src\common;
$(SolutionDir)src\;
$(SolutionDir)deps\spdlog\include;
./;
%(AdditionalIncludeDirectories)
</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>Advapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\notifications\notifications.vcxproj">
<Project>{1d5be09d-78c0-4fd7-af00-ae7c1af7c525}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\Telemetry\EtwTrace\EtwTrace.vcxproj">
<Project>{8f021b46-362b-485c-bfba-ccf83e820cbd}</Project>
</ProjectReference>
</ItemGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\..\common\SettingsAPI\FileWatcher.cpp" />
<ClCompile Include="..\..\..\common\SettingsAPI\settings_helpers.cpp" />
<ClCompile Include="..\..\..\common\SettingsAPI\settings_objects.cpp" />
<ClCompile Include="LightSwitchService.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="LightSwitchSettings.cpp" />
<ClCompile Include="SettingsConstants.cpp" />
<ClCompile Include="ThemeHelper.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="ThemeScheduler.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="WinHookEventIDs.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="LightSwitchSettings.h" />
<ClInclude Include="SettingsConstants.h" />
<ClInclude Include="SettingsObserver.h" />
<ClInclude Include="ThemeHelper.h" />
<ClInclude Include="ThemeScheduler.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">false</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="WinHookEventIDs.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="..\..\..\..\deps\spdlog.props" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
</Target>
</Project>

View File

@@ -1,72 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="LightSwitchService.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ThemeScheduler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ThemeHelper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\common\SettingsAPI\settings_helpers.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\common\SettingsAPI\settings_objects.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\common\SettingsAPI\FileWatcher.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="LightSwitchSettings.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SettingsConstants.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="WinHookEventIDs.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="ThemeScheduler.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ThemeHelper.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="LightSwitchSettings.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SettingsConstants.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SettingsObserver.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="WinHookEventIDs.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
</ItemGroup>
</Project>

View File

@@ -1,157 +0,0 @@
#include "LightSwitchSettings.h"
#include <common/utils/json.h>
#include <common/SettingsAPI/settings_helpers.h>
#include "SettingsObserver.h"
#include <filesystem>
#include <fstream>
#include <WinHookEventIDs.h>
using namespace std;
LightSwitchSettings& LightSwitchSettings::instance()
{
static LightSwitchSettings inst;
return inst;
}
LightSwitchSettings::LightSwitchSettings()
{
LoadSettings();
}
std::wstring LightSwitchSettings::GetSettingsFileName()
{
return PTSettingsHelper::get_module_save_file_location(L"LightSwitch");
}
void LightSwitchSettings::InitFileWatcher()
{
const std::wstring& settingsFileName = GetSettingsFileName();
m_settingsFileWatcher = std::make_unique<FileWatcher>(settingsFileName, [&]() {
PostMessageW(HWND_BROADCAST, WM_PRIV_SETTINGS_CHANGED, NULL, NULL);
});
}
void LightSwitchSettings::AddObserver(SettingsObserver& observer)
{
m_observers.insert(&observer);
}
void LightSwitchSettings::RemoveObserver(SettingsObserver& observer)
{
m_observers.erase(&observer);
}
void LightSwitchSettings::NotifyObservers(SettingId id) const
{
for (auto observer : m_observers)
{
if (observer->WantsToBeNotified(id))
{
observer->SettingsUpdate(id);
}
}
}
void LightSwitchSettings::LoadSettings()
{
try
{
PowerToysSettings::PowerToyValues values =
PowerToysSettings::PowerToyValues::load_from_settings_file(L"LightSwitch");
if (const auto jsonVal = values.get_string_value(L"scheduleMode"))
{
auto val = *jsonVal;
auto newMode = FromString(val);
if (m_settings.scheduleMode != newMode)
{
m_settings.scheduleMode = newMode;
NotifyObservers(SettingId::ScheduleMode);
}
}
// Latitude
if (const auto jsonVal = values.get_string_value(L"latitude"))
{
auto val = *jsonVal;
if (m_settings.latitude != val)
{
m_settings.latitude = val;
NotifyObservers(SettingId::Latitude);
}
}
// Longitude
if (const auto jsonVal = values.get_string_value(L"longitude"))
{
auto val = *jsonVal;
if (m_settings.longitude != val)
{
m_settings.longitude = val;
NotifyObservers(SettingId::Longitude);
}
}
// LightTime
if (const auto jsonVal = values.get_int_value(L"lightTime"))
{
auto val = *jsonVal;
if (m_settings.lightTime != val)
{
m_settings.lightTime = val;
NotifyObservers(SettingId::LightTime);
}
}
// DarkTime
if (const auto jsonVal = values.get_int_value(L"darkTime"))
{
auto val = *jsonVal;
if (m_settings.darkTime != val)
{
m_settings.darkTime = val;
NotifyObservers(SettingId::DarkTime);
}
}
// Offset
if (const auto jsonVal = values.get_int_value(L"offset"))
{
auto val = *jsonVal;
if (m_settings.offset != val)
{
m_settings.offset = val;
NotifyObservers(SettingId::Offset);
}
}
// ChangeSystem
if (const auto jsonVal = values.get_bool_value(L"changeSystem"))
{
auto val = *jsonVal;
if (m_settings.changeSystem != val)
{
m_settings.changeSystem = val;
NotifyObservers(SettingId::ChangeSystem);
}
}
// ChangeApps
if (const auto jsonVal = values.get_bool_value(L"changeApps"))
{
auto val = *jsonVal;
if (m_settings.changeApps != val)
{
m_settings.changeApps = val;
NotifyObservers(SettingId::ChangeApps);
}
}
}
catch (...)
{
// Keeps defaults if load fails
}
}

View File

@@ -1,87 +0,0 @@
#pragma once
#include <unordered_set>
#include <string>
#include <vector>
#include <memory>
#include <windows.h>
#include <common/SettingsAPI/FileWatcher.h>
#include <common/SettingsAPI/settings_objects.h>
#include <SettingsConstants.h>
class SettingsObserver;
enum class ScheduleMode
{
FixedHours,
SunsetToSunrise
// Add more in the future
};
inline std::wstring ToString(ScheduleMode mode)
{
switch (mode)
{
case ScheduleMode::FixedHours:
return L"FixedHours";
case ScheduleMode::SunsetToSunrise:
return L"SunsetToSunrise";
default:
return L"FixedHours";
}
}
inline ScheduleMode FromString(const std::wstring& str)
{
if (str == L"SunsetToSunrise")
return ScheduleMode::SunsetToSunrise;
else
return ScheduleMode::FixedHours;
}
struct LightSwitchConfig
{
ScheduleMode scheduleMode = ScheduleMode::FixedHours;
std::wstring latitude = L"0.0";
std::wstring longitude = L"0.0";
// Stored as minutes since midnight
int lightTime = 8 * 60; // 08:00 default
int darkTime = 20 * 60; // 20:00 default
int offset = 0; // offset in minutes to apply to calculated times
bool changeSystem = false;
bool changeApps = false;
};
class LightSwitchSettings
{
public:
static LightSwitchSettings& instance();
static inline const LightSwitchConfig& settings()
{
return instance().m_settings;
}
void InitFileWatcher();
static std::wstring GetSettingsFileName();
void AddObserver(SettingsObserver& observer);
void RemoveObserver(SettingsObserver& observer);
void LoadSettings();
private:
LightSwitchSettings();
~LightSwitchSettings() = default;
LightSwitchConfig m_settings;
std::unique_ptr<FileWatcher> m_settingsFileWatcher;
std::unordered_set<SettingsObserver*> m_observers;
void NotifyObservers(SettingId id) const;
};

View File

@@ -1 +0,0 @@
#include "SettingsConstants.h"

View File

@@ -1,13 +0,0 @@
#pragma once
enum class SettingId
{
ScheduleMode = 0,
Latitude,
Longitude,
LightTime,
DarkTime,
Offset,
ChangeSystem,
ChangeApps
};

View File

@@ -1,32 +0,0 @@
#pragma once
#include <unordered_set>
#include "SettingsConstants.h"
class LightSwitchSettings;
class SettingsObserver
{
public:
SettingsObserver(std::unordered_set<SettingId> observedSettings) :
m_observedSettings(std::move(observedSettings))
{
LightSwitchSettings::instance().AddObserver(*this);
}
virtual ~SettingsObserver()
{
LightSwitchSettings::instance().RemoveObserver(*this);
}
// Override this in your class to respond to updates
virtual void SettingsUpdate(SettingId type) {}
bool WantsToBeNotified(SettingId type) const noexcept
{
return m_observedSettings.contains(type);
}
protected:
std::unordered_set<SettingId> m_observedSettings;
};

View File

@@ -1,80 +0,0 @@
#include <windows.h>
#include "ThemeHelper.h"
// Controls changing the themes.
void SetAppsTheme(bool mode)
{
HKEY hKey;
if (RegOpenKeyEx(HKEY_CURRENT_USER,
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
0,
KEY_SET_VALUE,
&hKey) == ERROR_SUCCESS)
{
DWORD value = mode;
RegSetValueEx(hKey, L"AppsUseLightTheme", 0, REG_DWORD, reinterpret_cast<const BYTE*>(&value), sizeof(value));
RegCloseKey(hKey);
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, reinterpret_cast<LPARAM>(L"ImmersiveColorSet"), SMTO_ABORTIFHUNG, 5000, nullptr);
SendMessageTimeout(HWND_BROADCAST, WM_THEMECHANGED, 0, 0, SMTO_ABORTIFHUNG, 5000, nullptr);
}
}
void SetSystemTheme(bool mode)
{
HKEY hKey;
if (RegOpenKeyEx(HKEY_CURRENT_USER,
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
0,
KEY_SET_VALUE,
&hKey) == ERROR_SUCCESS)
{
DWORD value = mode;
RegSetValueEx(hKey, L"SystemUsesLightTheme", 0, REG_DWORD, reinterpret_cast<const BYTE*>(&value), sizeof(value));
RegCloseKey(hKey);
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, reinterpret_cast<LPARAM>(L"ImmersiveColorSet"), SMTO_ABORTIFHUNG, 5000, nullptr);
SendMessageTimeout(HWND_BROADCAST, WM_THEMECHANGED, 0, 0, SMTO_ABORTIFHUNG, 5000, nullptr);
}
}
bool GetCurrentSystemTheme()
{
HKEY hKey;
DWORD value = 1; // default = light
DWORD size = sizeof(value);
if (RegOpenKeyEx(HKEY_CURRENT_USER,
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
0,
KEY_READ,
&hKey) == ERROR_SUCCESS)
{
RegQueryValueEx(hKey, L"SystemUsesLightTheme", nullptr, nullptr, reinterpret_cast<LPBYTE>(&value), &size);
RegCloseKey(hKey);
}
return value == 1; // true = light, false = dark
}
bool GetCurrentAppsTheme()
{
HKEY hKey;
DWORD value = 1;
DWORD size = sizeof(value);
if (RegOpenKeyEx(HKEY_CURRENT_USER,
L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
0,
KEY_READ,
&hKey) == ERROR_SUCCESS)
{
RegQueryValueEx(hKey, L"AppsUseLightTheme", nullptr, nullptr, reinterpret_cast<LPBYTE>(&value), &size);
RegCloseKey(hKey);
}
return value == 1; // true = light, false = dark
}

View File

@@ -1,5 +0,0 @@
#pragma once
void SetSystemTheme(bool dark);
void SetAppsTheme(bool dark);
bool GetCurrentSystemTheme();
bool GetCurrentAppsTheme();

View File

@@ -1,89 +0,0 @@
#include "ThemeScheduler.h"
#include <utility>
SunTimes CalculateSunriseSunset(double latitude, double longitude, int year, int month, int day)
{
double zenith = 90.833;
int N1 = static_cast<int>(floor(275.0 * month / 9.0));
int N2 = static_cast<int>(floor((static_cast<double>(month) + 9) / 12.0));
int N3 = static_cast<int>(floor((1.0 + floor((year - 4.0 * floor(year / 4.0) + 2.0) / 3.0))));
int N = N1 - (N2 * N3) + day - 30;
auto calcTime = [&](bool sunrise) -> double {
double lngHour = longitude / 15.0;
double t = sunrise ? N + ((6 - lngHour) / 24) : N + ((18 - lngHour) / 24);
double M = (0.9856 * t) - 3.289;
double L = M + (1.916 * sin(deg2rad(M))) + (0.020 * sin(2 * deg2rad(M))) + 282.634;
if (L < 0)
L += 360;
if (L > 360)
L -= 360;
double RA = rad2deg(atan(0.91764 * tan(deg2rad(L))));
if (RA < 0)
RA += 360;
if (RA > 360)
RA -= 360;
double Lquadrant = floor(L / 90) * 90;
double RAquadrant = floor(RA / 90) * 90;
RA = RA + (Lquadrant - RAquadrant);
RA /= 15;
double sinDec = 0.39782 * sin(deg2rad(L));
double cosDec = cos(asin(sinDec));
double cosH = (cos(deg2rad(zenith)) - (sinDec * sin(deg2rad(latitude)))) / (cosDec * cos(deg2rad(latitude)));
if (cosH > 1 || cosH < -1)
return -1;
double H = sunrise ? 360 - rad2deg(acos(cosH)) : rad2deg(acos(cosH));
H /= 15;
double T = H + RA - (0.06571 * t) - 6.622;
double UT = T - lngHour;
while (UT < 0)
UT += 24;
while (UT >= 24)
UT -= 24;
return UT;
};
double riseUT = calcTime(true);
double setUT = calcTime(false);
auto toLocal = [](double UT) {
TIME_ZONE_INFORMATION tz;
DWORD state = GetTimeZoneInformation(&tz);
double totalBias = tz.Bias;
if (state == TIME_ZONE_ID_DAYLIGHT)
totalBias += tz.DaylightBias;
else if (state == TIME_ZONE_ID_STANDARD)
totalBias += tz.StandardBias;
double biasHours = -(totalBias / 60.0);
double localTime = UT + biasHours;
while (localTime < 0)
localTime += 24;
while (localTime >= 24)
localTime -= 24;
int hour = static_cast<int>(localTime);
int minute = static_cast<int>((localTime - hour) * 60);
return std::pair<int, int>{ hour, minute };
};
auto [riseHour, riseMinute] = toLocal(riseUT);
auto [setHour, setMinute] = toLocal(setUT);
SunTimes result;
result.sunriseHour = riseHour;
result.sunriseMinute = riseMinute;
result.sunsetHour = setHour;
result.sunsetMinute = setMinute;
return result;
}

View File

@@ -1,25 +0,0 @@
#pragma once
#include <cmath>
#include <ctime>
#include <windows.h>
// Struct to hold calculated sunrise/sunset times
struct SunTimes
{
int sunriseHour;
int sunriseMinute;
int sunsetHour;
int sunsetMinute;
};
constexpr double PI = 3.14159265358979323846;
constexpr double deg2rad(double deg)
{
return deg * PI / 180.0;
}
constexpr double rad2deg(double rad)
{
return rad * 180.0 / PI;
}
SunTimes CalculateSunriseSunset(double latitude, double longitude, int year, int month, int day);

View File

@@ -1,15 +0,0 @@
#include "WinHookEventIDs.h"
#include <wtypes.h>
#include <mutex>
UINT WM_PRIV_SETTINGS_CHANGED = 0;
std::once_flag init_flag;
void InitializeWinhookEventIds()
{
std::call_once(init_flag, [&] {
WM_PRIV_SETTINGS_CHANGED = RegisterWindowMessage(L"{11978F7B-221A-4E65-B9A9-693F7D6E4B25}");
});
}

View File

@@ -1,6 +0,0 @@
#pragma once
#include <Windows.h>
extern UINT WM_PRIV_SETTINGS_CHANGED; // Scheduled when a watched settings file is updated
void InitializeWinhookEventIds();

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
</packages>

View File

@@ -31,11 +31,7 @@ struct CommonState
Measurement::Unit units = Measurement::Unit::Pixel;
#pragma warning(push)
#pragma warning(disable : 4324)
alignas(8) POINT cursorPosSystemSpace = {}; // updated atomically
#pragma warning(pop)
POINT cursorPosSystemSpace = {}; // updated atomically
std::atomic_bool closeOnOtherMonitors = false;
float GetPhysicalPx2MmRatio(HWND window) const

View File

@@ -160,7 +160,7 @@ public partial class CommandItemViewModel : ExtensionObjectViewModel, ICommandBa
Initialized |= InitializedState.Initialized;
}
public virtual void SlowInitializeProperties()
public void SlowInitializeProperties()
{
if (IsSelectedInitialized)
{

View File

@@ -47,21 +47,9 @@ public partial class ListItemViewModel(IListItem model, WeakReference<IPageConte
UpdateTags(li.Tags);
TextToSuggest = li.TextToSuggest;
Section = li.Section ?? string.Empty;
UpdateProperty(nameof(Section));
}
public override void SlowInitializeProperties()
{
base.SlowInitializeProperties();
var model = Model.Unsafe;
if (model is null)
{
return;
}
var extensionDetails = model.Details;
var extensionDetails = li.Details;
if (extensionDetails is not null)
{
Details = new(extensionDetails, PageContext);
@@ -70,8 +58,8 @@ public partial class ListItemViewModel(IListItem model, WeakReference<IPageConte
UpdateProperty(nameof(HasDetails));
}
TextToSuggest = model.TextToSuggest;
UpdateProperty(nameof(TextToSuggest));
UpdateProperty(nameof(Section));
}
protected override void FetchProperty(string propertyName)

View File

@@ -3,12 +3,10 @@
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using CommunityToolkit.Mvvm.ComponentModel;
using ManagedCommon;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.Foundation;
@@ -41,7 +39,7 @@ public partial class AppStateModel : ObservableObject
{
if (string.IsNullOrEmpty(FilePath))
{
throw new InvalidOperationException($"You must set a valid {nameof(FilePath)} before calling {nameof(LoadState)}");
throw new InvalidOperationException($"You must set a valid {nameof(SettingsModel.FilePath)} before calling {nameof(LoadState)}");
}
if (!File.Exists(FilePath))
@@ -79,84 +77,43 @@ public partial class AppStateModel : ObservableObject
try
{
// Serialize the main dictionary to JSON and save it to the file
var settingsJson = JsonSerializer.Serialize(model, JsonSerializationContext.Default.AppStateModel!);
var settingsJson = JsonSerializer.Serialize(model, JsonSerializationContext.Default.AppStateModel);
// validate JSON
if (JsonNode.Parse(settingsJson) is not JsonObject newSettings)
// Is it valid JSON?
if (JsonNode.Parse(settingsJson) is JsonObject newSettings)
{
Logger.LogError("Failed to parse app state as a JsonObject.");
return;
}
// Now, read the existing content from the file
var oldContent = File.Exists(FilePath) ? File.ReadAllText(FilePath) : "{}";
// read previous settings
if (!TryReadSavedState(out var savedSettings))
{
savedSettings = new JsonObject();
}
// Is it valid JSON?
if (JsonNode.Parse(oldContent) is JsonObject savedSettings)
{
foreach (var item in newSettings)
{
savedSettings[item.Key] = item.Value?.DeepClone();
}
// merge new settings into old ones
foreach (var item in newSettings)
{
savedSettings[item.Key] = item.Value?.DeepClone();
}
var serialized = savedSettings.ToJsonString(JsonSerializationContext.Default.AppStateModel.Options);
File.WriteAllText(FilePath, serialized);
var serialized = savedSettings.ToJsonString(JsonSerializationContext.Default.AppStateModel!.Options);
File.WriteAllText(FilePath, serialized);
// TODO: Instead of just raising the event here, we should
// have a file change watcher on the settings file, and
// reload the settings then
model.StateChanged?.Invoke(model, null);
}
catch (Exception ex)
{
Logger.LogError($"Failed to save application state to {FilePath}:", ex);
}
}
private static bool TryReadSavedState([NotNullWhen(true)] out JsonObject? savedSettings)
{
savedSettings = null;
// read existing content from the file
string oldContent;
try
{
if (File.Exists(FilePath))
{
oldContent = File.ReadAllText(FilePath);
// TODO: Instead of just raising the event here, we should
// have a file change watcher on the settings file, and
// reload the settings then
model.StateChanged?.Invoke(model, null);
}
else
{
Debug.WriteLine("Failed to parse settings file as JsonObject.");
}
}
else
{
// file doesn't exist (might not have been created yet), so consider this a success
// and return empty settings
savedSettings = new JsonObject();
return true;
Debug.WriteLine("Failed to parse settings file as JsonObject.");
}
}
catch (Exception ex)
{
Logger.LogWarning($"Failed to read app state file {FilePath}:\n{ex}");
return false;
}
// detect empty file, just for sake of logging
if (string.IsNullOrWhiteSpace(oldContent))
{
Logger.LogInfo($"App state file is empty: {FilePath}");
return false;
}
// is it valid JSON?
try
{
savedSettings = JsonNode.Parse(oldContent) as JsonObject;
return savedSettings != null;
}
catch (Exception ex)
{
Logger.LogWarning($"Failed to parse app state from {FilePath}:\n{ex}");
return false;
Debug.WriteLine(ex.ToString());
}
}

View File

@@ -32,7 +32,6 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem
private string _generatedId = string.Empty;
private HotkeySettings? _hotkey;
private IIconInfo? _initialIcon;
private CommandAlias? Alias { get; set; }
@@ -58,8 +57,6 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem
public IIconInfo Icon => _commandItemViewModel.Icon;
public IIconInfo InitialIcon => _initialIcon ?? _commandItemViewModel.Icon;
ICommand? ICommandItem.Command => _commandItemViewModel.Command.Model.Unsafe;
IContextItem?[] ICommandItem.MoreCommands => _commandItemViewModel.MoreCommands
@@ -208,8 +205,6 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem
{
DisplayTitle = fallback.DisplayTitle;
}
UpdateInitialIcon(false);
}
}
@@ -226,31 +221,7 @@ public sealed partial class TopLevelViewModel : ObservableObject, IListItem
FetchAliasFromAliasManager();
UpdateHotkey();
UpdateTags();
UpdateInitialIcon();
}
else if (e.PropertyName == nameof(CommandItem.Icon))
{
UpdateInitialIcon();
}
}
}
private void UpdateInitialIcon(bool raiseNotification = true)
{
if (_initialIcon != null || !_commandItemViewModel.Icon.IsSet)
{
return;
}
_initialIcon = _commandItemViewModel.Icon;
if (raiseNotification)
{
DoOnUiThread(
() =>
{
PropChanged?.Invoke(this, new PropChangedEventArgs(nameof(InitialIcon)));
});
}
}

View File

@@ -85,7 +85,7 @@ public partial class App : Application
AppWindow = new MainWindow();
var activatedEventArgs = Microsoft.Windows.AppLifecycle.AppInstance.GetCurrent().GetActivatedEventArgs();
((MainWindow)AppWindow).HandleLaunchNonUI(activatedEventArgs);
((MainWindow)AppWindow).HandleLaunch(activatedEventArgs);
}
/// <summary>

View File

@@ -134,15 +134,6 @@ public sealed partial class CommandBar : UserControl,
WeakReferenceMessenger.Default.Send<OpenContextMenuMessage>(new OpenContextMenuMessage(null, null, null, ContextMenuFilterLocation.Bottom));
}
/// <summary>
/// Sets focus to the "More" button after closing the context menu,
/// keeping keyboard navigation intuitive.
/// </summary>
public void FocusMoreCommandsButton()
{
MoreCommandsButton?.Focus(FocusState.Programmatic);
}
private void ContextMenuFlyout_Opened(object sender, object e)
{
// We need to wait until our flyout is opened to try and toss focus

View File

@@ -15,7 +15,6 @@
xmlns:ui="using:CommunityToolkit.WinUI"
xmlns:viewModels="using:Microsoft.CmdPal.UI.ViewModels"
Background="Transparent"
PreviewKeyDown="UserControl_PreviewKeyDown"
mc:Ignorable="d">
<UserControl.Resources>

View File

@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.WinUI;
using Microsoft.CmdPal.Core.ViewModels;
using Microsoft.CmdPal.Core.ViewModels.Messages;
using Microsoft.CmdPal.UI.Messages;
@@ -116,24 +115,6 @@ public sealed partial class ContextMenu : UserControl,
}
}
/// <summary>
/// Handles Escape to close the context menu and return focus to the "More" button.
/// </summary>
private void UserControl_PreviewKeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key == VirtualKey.Escape)
{
// Close the context menu (if not already handled)
WeakReferenceMessenger.Default.Send(new CloseContextMenuMessage());
// Find the parent CommandBar and set focus to MoreCommandsButton
var parent = this.FindParent<CommandBar>();
parent?.FocusMoreCommandsButton();
e.Handled = true;
}
}
private void ViewModel_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
var prop = e.PropertyName;

View File

@@ -123,9 +123,6 @@ public sealed partial class MainWindow : WindowEx,
_localKeyboardListener = new LocalKeyboardListener();
_localKeyboardListener.KeyPressed += LocalKeyboardListener_OnKeyPressed;
_localKeyboardListener.Start();
// Force window to be created, and then cloaked. This will offset initial animation when the window is shown.
HideWindow();
}
private static void LocalKeyboardListener_OnKeyPressed(object? sender, LocalKeyboardListenerKeyPressedEventArgs e)
@@ -236,6 +233,9 @@ public sealed partial class MainWindow : WindowEx,
{
var hwnd = new HWND(hwndValue != 0 ? hwndValue : _hwnd);
// Make sure our HWND is cloaked before any possible window manipulations
Cloak();
// Remember, IsIconic == "minimized", which is entirely different state
// from "show/hide"
// If we're currently minimized, restore us first, before we reveal
@@ -243,9 +243,6 @@ public sealed partial class MainWindow : WindowEx,
// which would remain not visible to the user.
if (PInvoke.IsIconic(hwnd))
{
// Make sure our HWND is cloaked before any possible window manipulations
Cloak();
PInvoke.ShowWindow(hwnd, SHOW_WINDOW_CMD.SW_RESTORE);
}
@@ -484,13 +481,8 @@ public sealed partial class MainWindow : WindowEx,
}
}
public void HandleLaunchNonUI(AppActivationArguments? activatedEventArgs)
public void HandleLaunch(AppActivationArguments? activatedEventArgs)
{
// LOAD BEARING
// Any reading and processing of the activation arguments must be done
// synchronously in this method, before it returns. The sending instance
// remains blocked until this returns; afterward it may quit, causing
// the activation arguments to be lost.
if (activatedEventArgs is null)
{
Summon(string.Empty);
@@ -527,26 +519,9 @@ public sealed partial class MainWindow : WindowEx,
}
catch (COMException ex)
{
// https://learn.microsoft.com/en-us/windows/win32/rpc/rpc-return-values
const int RPC_S_SERVER_UNAVAILABLE = -2147023174;
const int RPC_S_CALL_FAILED = 2147023170;
// Accessing properties activatedEventArgs.Kind and activatedEventArgs.Data might cause COMException
// if the args are not valid or not passed correctly.
if (ex.HResult is RPC_S_SERVER_UNAVAILABLE or RPC_S_CALL_FAILED)
{
Logger.LogWarning(
$"COM exception (HRESULT {ex.HResult}) when accessing activation arguments. " +
$"This might be due to the calling application not passing them correctly or exiting before we could read them. " +
$"The application will continue running and fall back to showing the Command Palette window.");
}
else
{
Logger.LogError(
$"COM exception (HRESULT {ex.HResult}) when activating the application. " +
$"The application will continue running and fall back to showing the Command Palette window.",
ex);
}
Logger.LogError("COM exception when activating the application", ex);
}
Summon(string.Empty);
@@ -635,20 +610,6 @@ public sealed partial class MainWindow : WindowEx,
}
private void HandleSummon(string commandId)
{
if (_ignoreHotKeyWhenFullScreen)
{
// If we're in full screen mode, ignore the hotkey
if (WindowHelper.IsWindowFullscreen())
{
return;
}
}
HandleSummonCore(commandId);
}
private void HandleSummonCore(string commandId)
{
var isRootHotkey = string.IsNullOrEmpty(commandId);
PowerToysTelemetry.Log.WriteEvent(new CmdPalHotkeySummoned(isRootHotkey));
@@ -673,6 +634,8 @@ public sealed partial class MainWindow : WindowEx,
// so that we can bind hotkeys to individual commands
if (!isVisible || !isRootHotkey)
{
Activate();
Summon(commandId);
}
else if (isRootHotkey)
@@ -708,6 +671,15 @@ public sealed partial class MainWindow : WindowEx,
var hotkeyIndex = (int)wParam.Value;
if (hotkeyIndex < _hotkeys.Count)
{
if (_ignoreHotKeyWhenFullScreen)
{
// If we're in full screen mode, ignore the hotkey
if (WindowHelper.IsWindowFullscreen())
{
return (LRESULT)IntPtr.Zero;
}
}
var hotkey = _hotkeys[hotkeyIndex];
HandleSummon(hotkey.CommandId);
}

View File

@@ -107,33 +107,12 @@ internal sealed class Program
{
// Do the redirection on another thread, and use a non-blocking
// wait method to wait for the redirection to complete.
using var redirectSemaphore = new Semaphore(0, 1);
var redirectTimeout = TimeSpan.FromSeconds(32);
_ = Task.Run(() =>
var redirectSemaphore = new Semaphore(0, 1);
Task.Run(() =>
{
using var cts = new CancellationTokenSource(redirectTimeout);
try
{
keyInstance.RedirectActivationToAsync(args)
.AsTask(cts.Token)
.GetAwaiter()
.GetResult();
}
catch (OperationCanceledException)
{
Logger.LogError($"Failed to activate existing instance; timed out after {redirectTimeout}.");
}
catch (Exception ex)
{
Logger.LogError("Failed to activate existing instance", ex);
}
finally
{
redirectSemaphore.Release();
}
keyInstance.RedirectActivationToAsync(args).AsTask().Wait();
redirectSemaphore.Release();
});
_ = PInvoke.CoWaitForMultipleObjects(
(uint)CWMO_FLAGS.CWMO_DEFAULT,
PInvoke.INFINITE,
@@ -145,14 +124,13 @@ internal sealed class Program
{
// If we already have a form, display the message now.
// Otherwise, add it to the collection for displaying later.
if (App.Current?.AppWindow is MainWindow mainWindow)
if (App.Current is App thisApp)
{
// LOAD BEARING
// This must be synchronous to ensure the method does not return
// before the activation is fully handled and the parameters are processed.
// The sending instance remains blocked until this returns; afterward it may quit,
// causing the activation arguments to be lost.
mainWindow.HandleLaunchNonUI(args);
if (thisApp.AppWindow is not null and
MainWindow mainWindow)
{
uiContext?.Post(_ => mainWindow.HandleLaunch(args), null);
}
}
}
}

View File

@@ -125,7 +125,7 @@
Width="20"
Height="20"
AutomationProperties.AccessibilityView="Raw"
SourceKey="{x:Bind InitialIcon, Mode=OneWay}"
SourceKey="{x:Bind Icon, Mode=OneWay}"
SourceRequested="{x:Bind helpers:IconCacheProvider.SourceRequested}" />
</cpcontrols:ContentIcon.Content>
</cpcontrols:ContentIcon>

View File

@@ -24,15 +24,23 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TitleBar x:Name="TitleBar">
<!-- This is a workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/10374, once fixed we should just be using IconSource -->
<TitleBar.LeftHeader>
<ImageIcon
Height="16"
Margin="16,0,0,0"
Source="ms-appx:///Assets/icon.svg" />
</TitleBar.LeftHeader>
</TitleBar>
<!-- TO DO: Replace this with WinUI TitleBar once that ships. -->
<StackPanel
x:Name="AppTitleBar"
Grid.Row="0"
Height="48"
Margin="16,0,0,0"
Orientation="Horizontal">
<Image
Width="16"
Height="16"
Source="ms-appx:///Assets/icon.svg" />
<TextBlock
x:Uid="CmdPalSettingsHeader"
Margin="12,0,0,0"
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}" />
</StackPanel>
<NavigationView
x:Name="NavView"
Grid.Row="1"
@@ -69,6 +77,7 @@
x:Name="NavigationBreadcrumbBar"
Grid.Row="0"
MaxWidth="1000"
Margin="16,0,0,0"
ItemClicked="NavigationBreadcrumbBar_ItemClicked"
ItemsSource="{x:Bind BreadCrumbs, Mode=OneWay}">
<BreadcrumbBar.ItemTemplate>

View File

@@ -31,10 +31,8 @@ public sealed partial class SettingsWindow : WindowEx,
this.InitializeComponent();
this.ExtendsContentIntoTitleBar = true;
this.SetIcon();
var title = RS_.GetString("SettingsWindowTitle");
this.AppWindow.Title = title;
this.AppWindow.Title = RS_.GetString("SettingsWindowTitle");
this.AppWindow.TitleBar.PreferredHeightOption = TitleBarHeightOption.Tall;
this.TitleBar.Title = title;
PositionCentered();
WeakReferenceMessenger.Default.Register<NavigateToExtensionSettingsMessage>(this);

View File

@@ -15,13 +15,13 @@ public class MockSettingsInterface : ISettingsInterface
public bool GlobalIfURI { get; set; }
public uint HistoryItemCount { get; set; }
public string ShowHistory { get; set; }
public MockSettingsInterface(uint historyItemCount = 0, bool globalIfUri = true, List<HistoryItem> mockHistory = null)
public MockSettingsInterface(string showHistory = "none", bool globalIfUri = true, List<HistoryItem> mockHistory = null)
{
_historyItems = mockHistory ?? new List<HistoryItem>();
GlobalIfURI = globalIfUri;
HistoryItemCount = historyItemCount;
ShowHistory = showHistory;
}
public List<ListItem> LoadHistory()
@@ -50,9 +50,9 @@ public class MockSettingsInterface : ISettingsInterface
_historyItems.Add(historyItem);
// Simulate the same logic as SettingsManager
if (HistoryItemCount > 0)
if (int.TryParse(ShowHistory, out var maxHistoryItems) && maxHistoryItems > 0)
{
while (_historyItems.Count > HistoryItemCount)
while (_historyItems.Count > maxHistoryItems)
{
_historyItems.RemoveAt(0); // Remove the oldest item
}

View File

@@ -54,7 +54,7 @@ public class QueryTests : CommandPaletteUnitTestBase
new HistoryItem("another search", DateTime.Parse("2024-01-02 13:00:00", CultureInfo.CurrentCulture)),
};
var settings = new MockSettingsInterface(mockHistory: mockHistoryItems, historyItemCount: 5);
var settings = new MockSettingsInterface(mockHistory: mockHistoryItems, showHistory: "5");
var page = new WebSearchListPage(settings);
@@ -89,7 +89,7 @@ public class QueryTests : CommandPaletteUnitTestBase
new HistoryItem("another search4", DateTime.Parse("2024-01-05 13:00:00", CultureInfo.CurrentCulture)),
};
var settings = new MockSettingsInterface(mockHistory: mockHistoryItems, historyItemCount: 5);
var settings = new MockSettingsInterface(mockHistory: mockHistoryItems, showHistory: "5");
var page = new WebSearchListPage(settings);
@@ -122,7 +122,7 @@ public class QueryTests : CommandPaletteUnitTestBase
new HistoryItem("another search5", DateTime.Parse("2024-01-06 13:00:00", CultureInfo.CurrentCulture)),
};
var settings = new MockSettingsInterface(mockHistory: mockHistoryItems, historyItemCount: 0);
var settings = new MockSettingsInterface(mockHistory: mockHistoryItems, showHistory: "None");
var page = new WebSearchListPage(settings);

View File

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

View File

@@ -1,130 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ManagedCommon;
using Microsoft.CmdPal.Ext.Apps.Programs;
using Microsoft.CmdPal.Ext.Apps.Properties;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.Management.Deployment;
namespace Microsoft.CmdPal.Ext.Apps.Commands;
internal sealed partial class UninstallApplicationCommand : InvokableCommand
{
// This is a ms-settings URI that opens the Apps & Features page in Windows Settings.
// It's correct and follows the Microsoft documentation:
// https://learn.microsoft.com/en-us/windows/apps/develop/launch/launch-settings-app#apps
private const string AppsFeaturesUri = "ms-settings:appsfeatures";
private readonly UWPApplication? _uwpTarget;
private readonly Win32Program? _win32Target;
public UninstallApplicationCommand(UWPApplication target)
{
Name = Resources.uninstall_application;
Icon = Icons.UninstallApplicationIcon;
_uwpTarget = target ?? throw new ArgumentNullException(nameof(target));
}
public UninstallApplicationCommand(Win32Program target)
{
Name = Resources.uninstall_application;
Icon = Icons.UninstallApplicationIcon;
_win32Target = target ?? throw new ArgumentNullException(nameof(target));
}
private async Task<CommandResult> UninstallUwpAppAsync(UWPApplication app)
{
if (string.IsNullOrWhiteSpace(app.Package.FullName))
{
Logger.LogError($"Critical error while uninstalling: packageFullName cannot be null or empty.");
return CommandResult.ShowToast(new ToastArgs()
{
Message = string.Format(CultureInfo.CurrentCulture, CompositeFormat.Parse(Resources.uninstall_application_failed), app.DisplayName),
Result = CommandResult.KeepOpen(),
});
}
try
{
// Which timeout to use for the uninstallation operation?
using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(60)))
{
var packageManager = new PackageManager();
var result = await packageManager.RemovePackageAsync(app.Package.FullName).AsTask(cts.Token);
if (result.ErrorText is not null && result.ErrorText.Length > 0)
{
Logger.LogError($"Failed to uninstall {app.Package.FullName}: {result.ErrorText}");
return CommandResult.ShowToast(new ToastArgs()
{
Message = string.Format(CultureInfo.CurrentCulture, CompositeFormat.Parse(Resources.uninstall_application_failed), app.DisplayName),
Result = CommandResult.KeepOpen(),
});
}
}
// TODO: Update the Search results after uninstalling the app - unsure how to do this yet.
return CommandResult.ShowToast(new ToastArgs()
{
Message = string.Format(CultureInfo.CurrentCulture, CompositeFormat.Parse(Resources.uninstall_application_successful), app.DisplayName),
Result = CommandResult.GoHome(),
});
}
catch (OperationCanceledException)
{
Logger.LogError($"Timeout exceeded while uninstalling {app.Package.FullName}");
return CommandResult.ShowToast(new ToastArgs()
{
Message = string.Format(CultureInfo.CurrentCulture, CompositeFormat.Parse(Resources.uninstall_application_failed), app.DisplayName),
Result = CommandResult.KeepOpen(),
});
}
catch (UnauthorizedAccessException ex)
{
Logger.LogError($"Permission denied to uninstall {app.Package.FullName}. Elevated privileges may be required. Error: {ex.Message}");
return CommandResult.ShowToast(new ToastArgs()
{
Message = string.Format(CultureInfo.CurrentCulture, CompositeFormat.Parse(Resources.uninstall_application_failed), app.DisplayName),
Result = CommandResult.KeepOpen(),
});
}
catch (Exception ex)
{
Logger.LogError($"An unexpected error occurred during uninstallation of {app.Package.FullName}: {ex.Message}");
return CommandResult.ShowToast(new ToastArgs()
{
Message = string.Format(CultureInfo.CurrentCulture, CompositeFormat.Parse(Resources.uninstall_application_failed), app.DisplayName),
Result = CommandResult.KeepOpen(),
});
}
}
public override CommandResult Invoke()
{
if (_uwpTarget is not null)
{
return UninstallUwpAppAsync(_uwpTarget).ConfigureAwait(false).GetAwaiter().GetResult();
}
if (_win32Target is not null)
{
Process.Start(new ProcessStartInfo
{
FileName = AppsFeaturesUri,
UseShellExecute = true,
});
return CommandResult.Dismiss();
}
Logger.LogError("UninstallApplicationCommand invoked with no target.");
return CommandResult.Dismiss();
}
}

View File

@@ -1,66 +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.Globalization;
using System.Text;
using ManagedCommon;
using Microsoft.CmdPal.Ext.Apps.Programs;
using Microsoft.CmdPal.Ext.Apps.Properties;
using Microsoft.CommandPalette.Extensions.Toolkit;
namespace Microsoft.CmdPal.Ext.Apps.Commands;
internal sealed partial class UninstallApplicationConfirmation : InvokableCommand
{
private readonly UWPApplication? _uwpTarget;
private readonly Win32Program? _win32Target;
public UninstallApplicationConfirmation(UWPApplication target)
{
Name = Resources.uninstall_application;
Icon = Icons.UninstallApplicationIcon;
_uwpTarget = target ?? throw new ArgumentNullException(nameof(target));
}
public UninstallApplicationConfirmation(Win32Program target)
{
Name = Resources.uninstall_application;
Icon = Icons.UninstallApplicationIcon;
_win32Target = target ?? throw new ArgumentNullException(nameof(target));
}
public override CommandResult Invoke()
{
UninstallApplicationCommand uninstallCommand;
var applicationTitle = Resources.uninstall_application;
if (_uwpTarget is not null)
{
uninstallCommand = new UninstallApplicationCommand(_uwpTarget);
applicationTitle = _uwpTarget.DisplayName;
}
else if (_win32Target is not null)
{
uninstallCommand = new UninstallApplicationCommand(_win32Target);
applicationTitle = _win32Target.Name;
}
else
{
Logger.LogError("UninstallApplicationCommand invoked with no target.");
return CommandResult.Dismiss();
}
var confirmArgs = new ConfirmationArgs()
{
Title = string.Format(CultureInfo.CurrentCulture, CompositeFormat.Parse(Resources.uninstall_application_confirm_title), applicationTitle),
Description = Resources.uninstall_application_confirm_description,
PrimaryCommand = uninstallCommand,
IsPrimaryCommandCritical = true,
};
return CommandResult.Confirm(confirmArgs);
}
}

View File

@@ -21,6 +21,4 @@ internal sealed class Icons
public static IconInfo UnpinIcon { get; } = new("\uE77A"); // Unpin icon
public static IconInfo PinIcon { get; } = new("\uE840"); // Pin icon
public static IconInfo UninstallApplicationIcon { get; } = new("\uE74D"); // Uninstall icon
}

View File

@@ -117,14 +117,6 @@ public class UWPApplication : IUWPApplication
RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, shift: true, vkey: VirtualKey.R),
});
commands.Add(
new CommandContextItem(
new UninstallApplicationConfirmation(this))
{
RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, shift: true, vkey: VirtualKey.Delete),
IsCritical = true,
});
return commands;
}

View File

@@ -219,16 +219,6 @@ public class Win32Program : IProgram
RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, shift: true, vkey: VirtualKey.R),
});
if (AppType == ApplicationType.ShortcutApplication || AppType == ApplicationType.ApprefApplication || AppType == ApplicationType.Win32Application)
{
commands.Add(new CommandContextItem(
new UninstallApplicationConfirmation(this))
{
RequestedShortcut = KeyChordHelpers.FromModifiers(ctrl: true, shift: true, vkey: VirtualKey.Delete),
IsCritical = true,
});
}
return commands;
}

View File

@@ -223,7 +223,7 @@ namespace Microsoft.CmdPal.Ext.Apps.Properties {
}
/// <summary>
/// Looks up a localized string similar to Unlimited.
/// Looks up a localized string similar to None.
/// </summary>
internal static string limit_none {
get {
@@ -330,51 +330,6 @@ namespace Microsoft.CmdPal.Ext.Apps.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Uninstall.
/// </summary>
internal static string uninstall_application {
get {
return ResourceManager.GetString("uninstall_application", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to This app and its related information will be removed..
/// </summary>
internal static string uninstall_application_confirm_description {
get {
return ResourceManager.GetString("uninstall_application_confirm_description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Uninstall &quot;{0}&quot;?.
/// </summary>
internal static string uninstall_application_confirm_title {
get {
return ResourceManager.GetString("uninstall_application_confirm_title", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Error while uninstalling &apos;{0}&apos;.
/// </summary>
internal static string uninstall_application_failed {
get {
return ResourceManager.GetString("uninstall_application_failed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to &apos;{0}&apos; has been successfully uninstalled..
/// </summary>
internal static string uninstall_application_successful {
get {
return ResourceManager.GetString("uninstall_application_successful", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Unpin.
/// </summary>

View File

@@ -198,21 +198,6 @@
<data name="unpin_app" xml:space="preserve">
<value>Unpin</value>
</data>
<data name="uninstall_application" xml:space="preserve">
<value>Uninstall</value>
</data>
<data name="uninstall_application_successful" xml:space="preserve">
<value>'{0}' has been successfully uninstalled.</value>
</data>
<data name="uninstall_application_failed" xml:space="preserve">
<value>Error while uninstalling '{0}'</value>
</data>
<data name="uninstall_application_confirm_description" xml:space="preserve">
<value>This app and its related information will be removed.</value>
</data>
<data name="uninstall_application_confirm_title" xml:space="preserve">
<value>Uninstall "{0}"?</value>
</data>
<data name="limit_1" xml:space="preserve">
<value>1</value>
</data>

View File

@@ -17,7 +17,6 @@ internal sealed partial class FallbackSystemCommandItem : FallbackCommandItem
{
Title = string.Empty;
Subtitle = string.Empty;
Icon = Icons.LockIcon;
var isBootedInUefiMode = settings.GetSystemFirmwareType() == FirmwareType.Uefi;
var hideEmptyRB = settings.HideEmptyRecycleBin();

View File

@@ -22,7 +22,6 @@ internal sealed partial class FallbackTimeDateItem : FallbackCommandItem
{
Title = string.Empty;
Subtitle = string.Empty;
Icon = Icons.TimeDateIcon;
_settingsManager = settings;
_timestamp = timestamp;

View File

@@ -34,7 +34,7 @@ internal sealed partial class SearchWebCommand : InvokableCommand
return CommandResult.KeepOpen();
}
if (_settingsManager.HistoryItemCount != 0)
if (_settingsManager.ShowHistory != Resources.history_none)
{
_settingsManager.SaveHistory(new HistoryItem(Arguments, DateTime.Now));
}

View File

@@ -11,7 +11,7 @@ public interface ISettingsInterface
{
public bool GlobalIfURI { get; }
public uint HistoryItemCount { get; }
public string ShowHistory { get; }
public List<ListItem> LoadHistory();

View File

@@ -16,7 +16,6 @@ namespace Microsoft.CmdPal.Ext.WebSearch.Helpers;
public class SettingsManager : JsonSettingsManager, ISettingsInterface
{
private const string HistoryItemCountLegacySettingsKey = "ShowHistory";
private readonly string _historyPath;
private static readonly string _namespace = "websearch";
@@ -25,11 +24,11 @@ public class SettingsManager : JsonSettingsManager, ISettingsInterface
private static readonly List<ChoiceSetSetting.Choice> _choices =
[
new ChoiceSetSetting.Choice(Resources.history_none, "None"),
new ChoiceSetSetting.Choice(Resources.history_1, "1"),
new ChoiceSetSetting.Choice(Resources.history_5, "5"),
new ChoiceSetSetting.Choice(Resources.history_10, "10"),
new ChoiceSetSetting.Choice(Resources.history_20, "20"),
new ChoiceSetSetting.Choice(Resources.history_none, Resources.history_none),
new ChoiceSetSetting.Choice(Resources.history_1, Resources.history_1),
new ChoiceSetSetting.Choice(Resources.history_5, Resources.history_5),
new ChoiceSetSetting.Choice(Resources.history_10, Resources.history_10),
new ChoiceSetSetting.Choice(Resources.history_20, Resources.history_20),
];
private readonly ToggleSetting _globalIfURI = new(
@@ -38,15 +37,15 @@ public class SettingsManager : JsonSettingsManager, ISettingsInterface
Resources.plugin_global_if_uri,
false);
private readonly ChoiceSetSetting _historyItemCount = new(
Namespaced(HistoryItemCountLegacySettingsKey),
Resources.plugin_history_item_count,
Resources.plugin_history_item_count,
private readonly ChoiceSetSetting _showHistory = new(
Namespaced(nameof(ShowHistory)),
Resources.plugin_show_history,
Resources.plugin_show_history,
_choices);
public bool GlobalIfURI => _globalIfURI.Value;
public uint HistoryItemCount => uint.TryParse(_historyItemCount.Value, out var value) ? value : 0;
public string ShowHistory => _showHistory.Value ?? string.Empty;
internal static string SettingsJsonPath()
{
@@ -91,11 +90,11 @@ public class SettingsManager : JsonSettingsManager, ISettingsInterface
// Add the new history item
historyItems.Add(historyItem);
// Determine the maximum number of items to keep based on HistoryItemCount
if (HistoryItemCount > 0)
// Determine the maximum number of items to keep based on ShowHistory
if (int.TryParse(ShowHistory, out var maxHistoryItems) && maxHistoryItems > 0)
{
// Keep only the most recent `maxHistoryItems` items
while (historyItems.Count > HistoryItemCount)
while (historyItems.Count > maxHistoryItems)
{
historyItems.RemoveAt(0); // Remove the oldest item
}
@@ -151,7 +150,7 @@ public class SettingsManager : JsonSettingsManager, ISettingsInterface
_historyPath = HistoryStateJsonPath();
Settings.Add(_globalIfURI);
Settings.Add(_historyItemCount);
Settings.Add(_showHistory);
// Load settings from file upon initialization
LoadSettings();
@@ -189,11 +188,11 @@ public class SettingsManager : JsonSettingsManager, ISettingsInterface
base.SaveSettings();
try
{
if (HistoryItemCount == 0)
if (ShowHistory == Resources.history_none)
{
ClearHistory();
}
else if (HistoryItemCount > 0)
else if (int.TryParse(ShowHistory, out var maxHistoryItems) && maxHistoryItems > 0)
{
// Trim the history file if there are more items than the new limit
if (File.Exists(_historyPath))
@@ -202,10 +201,10 @@ public class SettingsManager : JsonSettingsManager, ISettingsInterface
var historyItems = JsonSerializer.Deserialize<List<HistoryItem>>(existingContent, WebSearchJsonSerializationContext.Default.ListHistoryItem) ?? [];
// Check if trimming is needed
if (historyItems.Count > HistoryItemCount)
if (historyItems.Count > maxHistoryItems)
{
// Trim the list to keep only the most recent `HistoryItemCount` items
historyItems = historyItems.Skip((int)(historyItems.Count - HistoryItemCount)).ToList();
// Trim the list to keep only the most recent `maxHistoryItems` items
historyItems = historyItems.Skip(historyItems.Count - maxHistoryItems).ToList();
// Save the trimmed history back to the file
var trimmedHistoryJson = JsonSerializer.Serialize(historyItems, WebSearchJsonSerializationContext.Default.ListHistoryItem);

View File

@@ -33,7 +33,7 @@ internal sealed partial class WebSearchListPage : DynamicListPage
_allItems = [];
Id = "com.microsoft.cmdpal.websearch";
_settingsManager = settingsManager;
_historyItems = _settingsManager.HistoryItemCount != 0 ? _settingsManager.LoadHistory() : null;
_historyItems = _settingsManager.ShowHistory != Resources.history_none ? _settingsManager.LoadHistory() : null;
if (_historyItems is not null)
{
_allItems.AddRange(_historyItems);
@@ -57,7 +57,7 @@ internal sealed partial class WebSearchListPage : DynamicListPage
if (_historyItems is not null)
{
filteredHistoryItems = _settingsManager.HistoryItemCount != 0 ? ListHelpers.FilterList(_historyItems, query).OfType<ListItem>() : null;
filteredHistoryItems = _settingsManager.ShowHistory != Resources.history_none ? ListHelpers.FilterList(_historyItems, query).OfType<ListItem>() : null;
}
var results = new List<ListItem>();

View File

@@ -168,15 +168,6 @@ namespace Microsoft.CmdPal.Ext.WebSearch.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Determines the number of history items to show from previous searches.
/// </summary>
public static string plugin_history_item_count {
get {
return ResourceManager.GetString("plugin_history_item_count", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to In the default browser.
/// </summary>
@@ -240,6 +231,15 @@ namespace Microsoft.CmdPal.Ext.WebSearch.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Determines the number of history items to show from previous searches.
/// </summary>
public static string plugin_show_history {
get {
return ResourceManager.GetString("plugin_show_history", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Settings.
/// </summary>

View File

@@ -172,7 +172,7 @@
<data name="plugin_search_failed" xml:space="preserve">
<value>Failed to open {0}.</value>
</data>
<data name="plugin_history_item_count" xml:space="preserve">
<data name="plugin_show_history" xml:space="preserve">
<value>Determines the number of history items to show from previous searches</value>
</data>
<data name="settings_page_name" xml:space="preserve">

View File

@@ -65,7 +65,6 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
if (_results is not null && _results.Count != 0)
{
var stopwatch = Stopwatch.StartNew();
var count = _results.Count;
var results = new ListItem[count];
var next = 0;
@@ -83,8 +82,6 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
}
}
stopwatch.Stop();
Logger.LogDebug($"Building ListItems took {stopwatch.ElapsedMilliseconds}ms", memberName: nameof(GetItems));
IsLoading = false;
return results;
}
@@ -247,22 +244,15 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
// foreach (var catalog in connections)
{
Stopwatch findPackages_stopwatch = new();
findPackages_stopwatch.Start();
Logger.LogDebug($" Searching {catalog.Info.Name} ({query})", memberName: nameof(DoSearchAsync));
ct.ThrowIfCancellationRequested();
Logger.LogDebug($"Preface for \"{searchDebugText}\" took {stopwatch.ElapsedMilliseconds}ms", memberName: nameof(DoSearchAsync));
// BODGY, re: microsoft/winget-cli#5151
// FindPackagesAsync isn't actually async.
var internalSearchTask = Task.Run(() => catalog.FindPackages(opts), ct);
var searchResults = await internalSearchTask;
findPackages_stopwatch.Stop();
Logger.LogDebug($"FindPackages for \"{searchDebugText}\" took {findPackages_stopwatch.ElapsedMilliseconds}ms", memberName: nameof(DoSearchAsync));
// TODO more error handling like this:
if (searchResults.Status != FindPackagesResultStatus.Ok)
{
@@ -271,8 +261,6 @@ internal sealed partial class WinGetExtensionPage : DynamicListPage, IDisposable
return [];
}
ct.ThrowIfCancellationRequested();
Logger.LogDebug($" got results for ({query})", memberName: nameof(DoSearchAsync));
// FYI Using .ToArray or any other kind of enumerable loop

View File

@@ -1194,6 +1194,15 @@ namespace Microsoft.CmdPal.Ext.WindowsSettings.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Dark mode.
/// </summary>
internal static string DarkMode {
get {
return ResourceManager.GetString("DarkMode", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Data usage.
/// </summary>
@@ -2121,15 +2130,6 @@ namespace Microsoft.CmdPal.Ext.WindowsSettings.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Light Switch.
/// </summary>
internal static string LightSwitch {
get {
return ResourceManager.GetString("LightSwitch", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Local Computer Policy.
/// </summary>

View File

@@ -572,8 +572,8 @@
<data name="DarkColor" xml:space="preserve">
<value>Dark color</value>
</data>
<data name="LightSwitch" xml:space="preserve">
<value>Light Switch</value>
<data name="DarkMode" xml:space="preserve">
<value>Dark mode</value>
</data>
<data name="DataUsage" xml:space="preserve">
<value>Data usage</value>

View File

@@ -1,324 +0,0 @@
<Project>
<PropertyGroup>
<AssemblyName>PowerToys.ImageResizer</AssemblyName>
<IntermediateOutputPath>obj\ARM64\Debug\</IntermediateOutputPath>
<BaseIntermediateOutputPath>obj\</BaseIntermediateOutputPath>
<MSBuildProjectExtensionsPath>D:\source\repos\PowerToys\src\modules\imageresizer\ui\obj\</MSBuildProjectExtensionsPath>
<_TargetAssemblyProjectName>ImageResizerUI</_TargetAssemblyProjectName>
<RootNamespace>ImageResizer</RootNamespace>
</PropertyGroup>
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<!-- Look at Directory.Build.props in root for common stuff as well -->
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
<Import Project="..\..\..\Common.SelfContained.props" />
<PropertyGroup>
<AssemblyTitle>PowerToys.ImageResizer</AssemblyTitle>
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps\</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
<UseWPF>true</UseWPF>
</PropertyGroup>
<PropertyGroup>
<ProjectGuid>{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>ImageResizer</RootNamespace>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>Resources\ImageResizer.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" />
<PackageReference Include="System.IO.Abstractions" />
<PackageReference Include="WPF-UI" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\GPOWrapperProjection\GPOWrapperProjection.csproj" />
<ProjectReference Include="..\..\..\common\interop\PowerToys.Interop.vcxproj" />
<ProjectReference Include="..\..\..\common\Common.UI\Common.UI.csproj" />
<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>
<ItemGroup>
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\Accessibility.dll" />
<ReferencePath Include="C:\Users\jaylynbarbee\.nuget\packages\controlzex\6.0.0\lib\net5.0-windows7.0\ControlzEx.dll" />
<ReferencePath Include="C:\Users\jaylynbarbee\.nuget\packages\microsoft.diagnostics.tracing.traceevent\3.1.16\lib\netstandard2.0\Dia2Lib.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\Microsoft.CSharp.dll" />
<ReferencePath Include="C:\Users\jaylynbarbee\.nuget\packages\microsoft.diagnostics.tracing.traceevent\3.1.16\lib\netstandard2.0\Microsoft.Diagnostics.FastSerialization.dll" />
<ReferencePath Include="C:\Users\jaylynbarbee\.nuget\packages\microsoft.diagnostics.netcore.client\0.2.510501\lib\net6.0\Microsoft.Diagnostics.NETCore.Client.dll" />
<ReferencePath Include="C:\Users\jaylynbarbee\.nuget\packages\microsoft.diagnostics.tracing.traceevent\3.1.16\lib\netstandard2.0\Microsoft.Diagnostics.Tracing.TraceEvent.dll" />
<ReferencePath Include="C:\Users\jaylynbarbee\.nuget\packages\microsoft.extensions.dependencyinjection.abstractions\9.0.8\lib\net9.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll" />
<ReferencePath Include="C:\Users\jaylynbarbee\.nuget\packages\microsoft.extensions.dependencyinjection\9.0.8\lib\net9.0\Microsoft.Extensions.DependencyInjection.dll" />
<ReferencePath Include="C:\Users\jaylynbarbee\.nuget\packages\microsoft.extensions.logging.abstractions\9.0.8\lib\net9.0\Microsoft.Extensions.Logging.Abstractions.dll" />
<ReferencePath Include="C:\Users\jaylynbarbee\.nuget\packages\microsoft.extensions.logging\9.0.8\lib\net9.0\Microsoft.Extensions.Logging.dll" />
<ReferencePath Include="C:\Users\jaylynbarbee\.nuget\packages\microsoft.extensions.options\9.0.8\lib\net9.0\Microsoft.Extensions.Options.dll" />
<ReferencePath Include="C:\Users\jaylynbarbee\.nuget\packages\microsoft.extensions.primitives\9.0.8\lib\net9.0\Microsoft.Extensions.Primitives.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\Microsoft.VisualBasic.Core.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\Microsoft.VisualBasic.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\Microsoft.VisualBasic.Forms.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\Microsoft.Win32.Primitives.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\Microsoft.Win32.Registry.AccessControl.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\Microsoft.Win32.Registry.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\Microsoft.Win32.SystemEvents.dll" />
<ReferencePath Include="C:\Users\jaylynbarbee\.nuget\packages\microsoft.windows.sdk.net.ref\10.0.26100.68-preview\lib\net8.0\Microsoft.Windows.SDK.NET.dll" />
<ReferencePath Include="C:\Users\jaylynbarbee\.nuget\packages\microsoft.xaml.behaviors.wpf\1.1.39\lib\net5.0-windows7.0\Microsoft.Xaml.Behaviors.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\mscorlib.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\netstandard.dll" />
<ReferencePath Include="D:\source\repos\PowerToys\src\common\Common.UI\bin\ARM64\Debug\net9.0-windows10.0.26100.0\PowerToys.Common.UI.dll" />
<ReferencePath Include="D:\source\repos\PowerToys\src\common\GPOWrapperProjection\bin\ARM64\Debug\net9.0-windows10.0.26100.0\PowerToys.GPOWrapperProjection.dll" />
<ReferencePath Include="D:\source\repos\PowerToys\src\common\ManagedCommon\bin\ARM64\Debug\net9.0-windows10.0.26100.0\PowerToys.ManagedCommon.dll" />
<ReferencePath Include="D:\source\repos\PowerToys\src\common\ManagedTelemetry\Telemetry\bin\ARM64\Debug\net9.0-windows10.0.26100.0\PowerToys.ManagedTelemetry.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\PresentationCore.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\PresentationFramework.Aero.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\PresentationFramework.Aero2.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\PresentationFramework.AeroLite.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\PresentationFramework.Classic.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\PresentationFramework.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\PresentationFramework.Luna.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\PresentationFramework.Royale.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\PresentationUI.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\ReachFramework.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.AppContext.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Buffers.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.CodeDom.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Collections.Concurrent.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Collections.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Collections.Immutable.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Collections.NonGeneric.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Collections.Specialized.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.ComponentModel.Annotations.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.ComponentModel.DataAnnotations.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.ComponentModel.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.ComponentModel.EventBasedAsync.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.ComponentModel.Primitives.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.ComponentModel.TypeConverter.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Configuration.ConfigurationManager.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Configuration.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Console.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Core.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Data.Common.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Data.DataSetExtensions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Data.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Design.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Diagnostics.Contracts.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Diagnostics.Debug.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Diagnostics.DiagnosticSource.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Diagnostics.EventLog.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Diagnostics.FileVersionInfo.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Diagnostics.PerformanceCounter.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Diagnostics.Process.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Diagnostics.StackTrace.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Diagnostics.TextWriterTraceListener.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Diagnostics.Tools.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Diagnostics.TraceSource.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Diagnostics.Tracing.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.DirectoryServices.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Drawing.Common.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Drawing.Design.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Drawing.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Drawing.Primitives.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Dynamic.Runtime.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Formats.Asn1.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Formats.Nrbf.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Formats.Tar.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Globalization.Calendars.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Globalization.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Globalization.Extensions.dll" />
<ReferencePath Include="C:\Users\jaylynbarbee\.nuget\packages\system.io.abstractions\22.0.13\lib\net9.0\System.IO.Abstractions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.IO.Compression.Brotli.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.IO.Compression.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.IO.Compression.FileSystem.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.IO.Compression.ZipFile.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.IO.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.IO.FileSystem.AccessControl.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.IO.FileSystem.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.IO.FileSystem.DriveInfo.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.IO.FileSystem.Primitives.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.IO.FileSystem.Watcher.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.IO.IsolatedStorage.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.IO.MemoryMappedFiles.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.IO.Packaging.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.IO.Pipelines.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.IO.Pipes.AccessControl.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.IO.Pipes.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.IO.UnmanagedMemoryStream.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Linq.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Linq.Expressions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Linq.Parallel.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Linq.Queryable.dll" />
<ReferencePath Include="C:\Users\jaylynbarbee\.nuget\packages\system.management\9.0.8\lib\net9.0\System.Management.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Memory.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Net.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Net.Http.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Net.Http.Json.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Net.HttpListener.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Net.Mail.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Net.NameResolution.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Net.NetworkInformation.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Net.Ping.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Net.Primitives.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Net.Quic.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Net.Requests.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Net.Security.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Net.ServicePoint.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Net.Sockets.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Net.WebClient.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Net.WebHeaderCollection.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Net.WebProxy.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Net.WebSockets.Client.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Net.WebSockets.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Numerics.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Numerics.Vectors.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.ObjectModel.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Printing.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Private.Windows.Core.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Reflection.DispatchProxy.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Reflection.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Reflection.Emit.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Reflection.Emit.ILGeneration.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Reflection.Emit.Lightweight.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Reflection.Extensions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Reflection.Metadata.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Reflection.Primitives.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Reflection.TypeExtensions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Resources.Extensions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Resources.Reader.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Resources.ResourceManager.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Resources.Writer.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Runtime.CompilerServices.Unsafe.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Runtime.CompilerServices.VisualC.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Runtime.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Runtime.Extensions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Runtime.Handles.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Runtime.InteropServices.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Runtime.InteropServices.JavaScript.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Runtime.InteropServices.RuntimeInformation.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Runtime.Intrinsics.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Runtime.Loader.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Runtime.Numerics.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Runtime.Serialization.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Runtime.Serialization.Formatters.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Runtime.Serialization.Json.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Runtime.Serialization.Primitives.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Runtime.Serialization.Xml.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Security.AccessControl.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Security.Claims.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Security.Cryptography.Algorithms.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Security.Cryptography.Cng.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Security.Cryptography.Csp.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Security.Cryptography.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Security.Cryptography.Encoding.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Security.Cryptography.OpenSsl.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Security.Cryptography.Pkcs.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Security.Cryptography.Primitives.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Security.Cryptography.ProtectedData.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Security.Cryptography.X509Certificates.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Security.Cryptography.Xml.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Security.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Security.Permissions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Security.Principal.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Security.Principal.Windows.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Security.SecureString.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.ServiceModel.Web.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.ServiceProcess.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Text.Encoding.CodePages.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Text.Encoding.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Text.Encoding.Extensions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Text.Encodings.Web.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Text.Json.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Text.RegularExpressions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Threading.AccessControl.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Threading.Channels.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Threading.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Threading.Overlapped.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Threading.Tasks.Dataflow.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Threading.Tasks.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Threading.Tasks.Extensions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Threading.Tasks.Parallel.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Threading.Thread.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Threading.ThreadPool.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Threading.Timer.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Transactions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Transactions.Local.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.ValueTuple.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Web.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Web.HttpUtility.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Windows.Controls.Ribbon.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Windows.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Windows.Extensions.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Windows.Forms.Design.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Windows.Forms.Design.Editors.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Windows.Forms.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Windows.Forms.Primitives.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Windows.Input.Manipulations.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Windows.Presentation.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\System.Xaml.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Xml.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Xml.Linq.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Xml.ReaderWriter.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Xml.Serialization.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Xml.XDocument.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Xml.XmlDocument.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Xml.XmlSerializer.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Xml.XPath.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\ref\net9.0\System.Xml.XPath.XDocument.dll" />
<ReferencePath Include="C:\Users\jaylynbarbee\.nuget\packages\testableio.system.io.abstractions\22.0.13\lib\net9.0\TestableIO.System.IO.Abstractions.dll" />
<ReferencePath Include="C:\Users\jaylynbarbee\.nuget\packages\testableio.system.io.abstractions.wrappers\22.0.13\lib\net9.0\TestableIO.System.IO.Abstractions.Wrappers.dll" />
<ReferencePath Include="C:\Users\jaylynbarbee\.nuget\packages\testably.abstractions.filesystem.interface\9.0.0\lib\net9.0\Testably.Abstractions.FileSystem.Interface.dll" />
<ReferencePath Include="C:\Users\jaylynbarbee\.nuget\packages\microsoft.diagnostics.tracing.traceevent\3.1.16\lib\netstandard2.0\TraceReloggerLib.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\UIAutomationClient.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\UIAutomationClientSideProviders.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\UIAutomationProvider.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\UIAutomationTypes.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\WindowsBase.dll" />
<ReferencePath Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\ref\net9.0\WindowsFormsIntegration.dll" />
<ReferencePath Include="C:\Users\jaylynbarbee\.nuget\packages\microsoft.windows.sdk.net.ref\10.0.26100.68-preview\lib\net8.0\WinRT.Runtime.dll" />
<ReferencePath Include="C:\Users\jaylynbarbee\.nuget\packages\wpf-ui\3.0.5\lib\net8.0-windows7.0\Wpf.Ui.dll" />
</ItemGroup>
<ItemGroup>
<Compile Include="D:\source\repos\PowerToys\src\modules\imageresizer\ui\obj\ARM64\Debug\Views\InputPage.g.cs" />
<Compile Include="D:\source\repos\PowerToys\src\modules\imageresizer\ui\obj\ARM64\Debug\Views\MainWindow.g.cs" />
<Compile Include="D:\source\repos\PowerToys\src\modules\imageresizer\ui\obj\ARM64\Debug\Views\ProgressPage.g.cs" />
<Compile Include="D:\source\repos\PowerToys\src\modules\imageresizer\ui\obj\ARM64\Debug\Views\ResultsPage.g.cs" />
<Compile Include="D:\source\repos\PowerToys\src\modules\imageresizer\ui\obj\ARM64\Debug\App.g.cs" />
<Compile Include="D:\source\repos\PowerToys\src\modules\imageresizer\ui\obj\ARM64\Debug\GeneratedInternalTypeHelper.g.cs" />
</ItemGroup>
<ItemGroup>
<Analyzer Include="C:\Users\jaylynbarbee\.nuget\packages\microsoft.codeanalysis.netanalyzers\9.0.0\analyzers\dotnet\cs\Microsoft.CodeAnalysis.CSharp.NetAnalyzers.dll" />
<Analyzer Include="C:\Users\jaylynbarbee\.nuget\packages\microsoft.codeanalysis.netanalyzers\9.0.0\analyzers\dotnet\cs\Microsoft.CodeAnalysis.NetAnalyzers.dll" />
<Analyzer Include="C:\Users\jaylynbarbee\.nuget\packages\microsoft.extensions.logging.abstractions\9.0.8\analyzers\dotnet\roslyn4.4\cs\Microsoft.Extensions.Logging.Generators.dll" />
<Analyzer Include="C:\Users\jaylynbarbee\.nuget\packages\microsoft.extensions.options\9.0.8\analyzers\dotnet\roslyn4.4\cs\Microsoft.Extensions.Options.SourceGeneration.dll" />
<Analyzer Include="C:\Users\jaylynbarbee\.nuget\packages\stylecop.analyzers.unstable\1.2.0.556\analyzers\dotnet\cs\StyleCop.Analyzers.CodeFixes.dll" />
<Analyzer Include="C:\Users\jaylynbarbee\.nuget\packages\stylecop.analyzers.unstable\1.2.0.556\analyzers\dotnet\cs\StyleCop.Analyzers.dll" />
<Analyzer Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\analyzers/dotnet/cs/Microsoft.Interop.ComInterfaceGenerator.dll" />
<Analyzer Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\analyzers/dotnet/cs/Microsoft.Interop.JavaScript.JSImportGenerator.dll" />
<Analyzer Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\analyzers/dotnet/cs/Microsoft.Interop.LibraryImportGenerator.dll" />
<Analyzer Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\analyzers/dotnet/cs/Microsoft.Interop.SourceGeneration.dll" />
<Analyzer Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\analyzers/dotnet/cs/System.Text.Json.SourceGeneration.dll" />
<Analyzer Include="C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\9.0.8\analyzers/dotnet/cs/System.Text.RegularExpressions.Generator.dll" />
<Analyzer Include="C:\Users\jaylynbarbee\.nuget\packages\microsoft.windows.sdk.net.ref\10.0.26100.68-preview\analyzers/dotnet/cs/WinRT.SourceGenerator.dll" />
<Analyzer Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\analyzers/dotnet/System.Windows.Forms.Analyzers.dll" />
<Analyzer Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\analyzers/dotnet/cs/System.Windows.Forms.Analyzers.CSharp.dll" />
<Analyzer Include="C:\Program Files\dotnet\packs\Microsoft.WindowsDesktop.App.Ref\9.0.8\analyzers/dotnet/cs/System.Windows.Forms.Analyzers.CodeFixes.CSharp.dll" />
</ItemGroup>
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
</Project>

View File

@@ -14,8 +14,7 @@ namespace Microsoft.PowerToys.Run.Plugin.PowerToys.Components
ShortcutGuide = 5,
RegistryPreview = 6,
CropAndLock = 7,
LightSwitch = 8,
EnvironmentVariables = 9,
Workspaces = 10,
EnvironmentVariables = 8,
Workspaces = 9,
}
}

View File

@@ -77,18 +77,7 @@ namespace Microsoft.PowerToys.Run.Plugin.PowerToys.Properties {
return ResourceManager.GetString("Action_Run_As_Administrator", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Light Switch.
/// </summary>
internal static string Light_Switch
{
get
{
return ResourceManager.GetString("Light_Switch", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Color Picker.
/// </summary>

View File

@@ -572,8 +572,8 @@
<data name="DarkColor" xml:space="preserve">
<value>Dark color</value>
</data>
<data name="LightSwitch" xml:space="preserve">
<value>Light Switch</value>
<data name="DarkMode" xml:space="preserve">
<value>Dark mode</value>
</data>
<data name="DataUsage" xml:space="preserve">
<value>Data usage</value>

View File

@@ -15,10 +15,10 @@ namespace RegistryPreview
/// </summary>
private void AppWindow_Closing(Microsoft.UI.Windowing.AppWindow sender, Microsoft.UI.Windowing.AppWindowClosingEventArgs args)
{
jsonWindowPlacement.SetNamedValue("appWindow.Position.X", JsonValue.CreateNumberValue(AppWindow.Position.X));
jsonWindowPlacement.SetNamedValue("appWindow.Position.Y", JsonValue.CreateNumberValue(AppWindow.Position.Y));
jsonWindowPlacement.SetNamedValue("appWindow.Size.Width", JsonValue.CreateNumberValue(AppWindow.Size.Width));
jsonWindowPlacement.SetNamedValue("appWindow.Size.Height", JsonValue.CreateNumberValue(AppWindow.Size.Height));
jsonWindowPlacement.SetNamedValue("appWindow.Position.X", JsonValue.CreateNumberValue(appWindow.Position.X));
jsonWindowPlacement.SetNamedValue("appWindow.Position.Y", JsonValue.CreateNumberValue(appWindow.Position.Y));
jsonWindowPlacement.SetNamedValue("appWindow.Size.Width", JsonValue.CreateNumberValue(appWindow.Size.Width));
jsonWindowPlacement.SetNamedValue("appWindow.Size.Height", JsonValue.CreateNumberValue(appWindow.Size.Height));
}
/// <summary>

View File

@@ -15,19 +15,38 @@
<Window.SystemBackdrop>
<MicaBackdrop />
</Window.SystemBackdrop>
<Grid x:Name="MainGrid" Loaded="Grid_Loaded">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TitleBar x:Name="titleBar">
<!-- This is a workaround for https://github.com/microsoft/microsoft-ui-xaml/issues/10374, once fixed we should just be using IconSource -->
<TitleBar.LeftHeader>
<ImageIcon
Height="16"
Margin="16,0,0,0"
Source="/Assets/RegistryPreview/RegistryPreview.ico" />
</TitleBar.LeftHeader>
</TitleBar>
<Grid
x:Name="titleBar"
Grid.Row="0"
Height="32"
Margin="16,0"
ColumnSpacing="16"
IsHitTestVisible="True">
<Grid.ColumnDefinitions>
<!--<ColumnDefinition x:Name="LeftPaddingColumn" Width="0"/>-->
<ColumnDefinition x:Name="IconColumn" Width="Auto" />
<ColumnDefinition x:Name="TitleColumn" Width="Auto" />
<!--<ColumnDefinition x:Name="RightPaddingColumn" Width="0"/>-->
</Grid.ColumnDefinitions>
<Image
Grid.Column="0"
Width="16"
Height="16"
VerticalAlignment="Center"
Source="../Assets/RegistryPreview/RegistryPreview.ico" />
<TextBlock
x:Name="titleBarText"
Grid.Column="1"
VerticalAlignment="Center"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{Binding ApplicationTitle}" />
</Grid>
</Grid>
</winuiex:WindowEx>
</winuiex:WindowEx>

View File

@@ -6,7 +6,6 @@ using System;
using ManagedCommon;
using Microsoft.PowerToys.Telemetry;
using Microsoft.UI;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
@@ -24,6 +23,7 @@ namespace RegistryPreview
private const string APPNAME = "RegistryPreview";
// private members
private Microsoft.UI.Windowing.AppWindow appWindow;
private JsonObject jsonWindowPlacement;
private string settingsFolder = string.Empty;
private string windowPlacementFile = "app-placement.json";
@@ -38,15 +38,20 @@ namespace RegistryPreview
settingsFolder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"\Microsoft\PowerToys\" + APPNAME;
OpenWindowPlacementFile(settingsFolder, windowPlacementFile);
// Update the Win32 looking window with the correct icon (and grab the appWindow handle for later)
IntPtr windowHandle = this.GetWindowHandle();
WindowId windowId = Win32Interop.GetWindowIdFromWindow(windowHandle);
appWindow = Microsoft.UI.Windowing.AppWindow.GetFromWindowId(windowId);
appWindow.SetIcon("Assets\\RegistryPreview\\RegistryPreview.ico");
// TODO(stefan)
AppWindow.Closing += AppWindow_Closing;
appWindow.Closing += AppWindow_Closing;
Activated += MainWindow_Activated;
// Extend the canvas to include the title bar so the app can support theming
ExtendsContentIntoTitleBar = true;
IntPtr windowHandle = this.GetWindowHandle();
WindowHelpers.ForceTopBorder1PixelInsetOnWindows10(windowHandle);
SetTitleBar(titleBar);
AppWindow.SetIcon("Assets\\RegistryPreview\\RegistryPreview.ico");
// if have settings, update the location of the window
if (jsonWindowPlacement != null)
@@ -61,7 +66,7 @@ namespace RegistryPreview
// check to make sure the size values are reasonable before attempting to restore the last saved size
if (size.Width >= 320 && size.Height >= 240)
{
AppWindow.Resize(size);
appWindow.Resize(size);
}
}
@@ -75,7 +80,7 @@ namespace RegistryPreview
// check to make sure the move values are reasonable before attempting to restore the last saved location
if (point.X >= 0 && point.Y >= 0)
{
AppWindow.Move(point);
appWindow.Move(point);
}
}
}
@@ -87,6 +92,20 @@ namespace RegistryPreview
PowerToysTelemetry.Log.WriteEvent(new RegistryPreviewEditorStartFinishEvent() { TimeStamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() });
}
private void MainWindow_Activated(object sender, WindowActivatedEventArgs args)
{
if (args.WindowActivationState == WindowActivationState.Deactivated)
{
titleBarText.Foreground =
(SolidColorBrush)Application.Current.Resources["WindowCaptionForegroundDisabled"];
}
else
{
titleBarText.Foreground =
(SolidColorBrush)Application.Current.Resources["WindowCaptionForeground"];
}
}
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
MainGrid.Children.Add(MainPage);
@@ -99,23 +118,23 @@ namespace RegistryPreview
if (string.IsNullOrEmpty(filename))
{
titleBar.Title = APPNAME;
AppWindow.Title = APPNAME;
titleBarText.Text = APPNAME;
appWindow.Title = APPNAME;
}
else
{
string[] file = filename.Split('\\');
if (file.Length > 0)
{
titleBar.Title = file[file.Length - 1] + " - " + APPNAME;
titleBarText.Text = file[file.Length - 1] + " - " + APPNAME;
}
else
{
titleBar.Title = filename + " - " + APPNAME;
titleBarText.Text = filename + " - " + APPNAME;
}
// Continue to update the window's title, after updating the custom title bar
AppWindow.Title = titleBar.Title;
appWindow.Title = titleBarText.Text;
}
}
}

View File

@@ -177,7 +177,6 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow
L"PowerToys.WorkspacesModuleInterface.dll",
L"PowerToys.CmdPalModuleInterface.dll",
L"PowerToys.ZoomItModuleInterface.dll",
L"PowerToys.LightSwitchModuleInterface.dll",
};
for (auto moduleSubdir : knownModules)

View File

@@ -757,8 +757,6 @@ std::string ESettingsWindowNames_to_string(ESettingsWindowNames value)
return "ColorPicker";
case ESettingsWindowNames::CmdNotFound:
return "CmdNotFound";
case ESettingsWindowNames::LightSwitch:
return "LightSwitch";
case ESettingsWindowNames::FancyZones:
return "FancyZones";
case ESettingsWindowNames::FileLocksmith:
@@ -844,10 +842,6 @@ ESettingsWindowNames ESettingsWindowNames_from_string(std::string value)
{
return ESettingsWindowNames::CmdNotFound;
}
else if (value == "LightSwitch")
{
return ESettingsWindowNames::LightSwitch;
}
else if (value == "FancyZones")
{
return ESettingsWindowNames::FancyZones;

View File

@@ -10,7 +10,6 @@ enum class ESettingsWindowNames
Awake,
ColorPicker,
CmdNotFound,
LightSwitch,
FancyZones,
FileLocksmith,
Run,

View File

@@ -513,23 +513,6 @@ namespace Microsoft.PowerToys.Settings.UI.Library
}
}
private bool lightSwitch;
[JsonPropertyName("LightSwitch")]
public bool LightSwitch
{
get => lightSwitch;
set
{
if (lightSwitch != value)
{
LogTelemetryEvent(value);
lightSwitch = value;
NotifyChange();
}
}
}
private void NotifyChange()
{
notifyEnabledChangedAction?.Invoke();

View File

@@ -1,19 +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.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Settings.UI.Library.Helpers
{
public sealed record City(string Name, string Country, double Latitude, double Longitude)
{
public string Display => string.IsNullOrWhiteSpace(Country)
? Name
: $"{Name}, {Country}";
}
}

View File

@@ -1,122 +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.Globalization;
using System.IO;
using System.Text;
using Settings.UI.Library.Helpers;
namespace Microsoft.PowerToys.Settings.UI.Helpers
{
public static class CityLoader
{
/// <summary>
/// Loads all cities from a world_cities.csv file.
/// Expected columns: city, city_ascii, lat, lng, country
/// </summary>
public static IEnumerable<City> LoadCities(string path)
{
using StreamReader reader = new(path);
// Read header
string header = reader.ReadLine();
if (header is null)
{
yield break;
}
List<string> headerCols = SplitCsvLine(header);
int idxCity = headerCols.FindIndex(h => string.Equals(h, "city", StringComparison.OrdinalIgnoreCase));
int idxCityAscii = headerCols.FindIndex(h => string.Equals(h, "city_ascii", StringComparison.OrdinalIgnoreCase));
int idxLat = headerCols.FindIndex(h => string.Equals(h, "lat", StringComparison.OrdinalIgnoreCase));
int idxLng = headerCols.FindIndex(h => string.Equals(h, "lng", StringComparison.OrdinalIgnoreCase));
int idxCountry = headerCols.FindIndex(h => string.Equals(h, "country", StringComparison.OrdinalIgnoreCase));
string line;
while ((line = reader.ReadLine()) is not null)
{
if (string.IsNullOrWhiteSpace(line))
{
continue;
}
List<string> cols = SplitCsvLine(line);
string rawCity = idxCity >= 0 && idxCity < cols.Count ? cols[idxCity] : string.Empty;
string cityAscii = idxCityAscii >= 0 && idxCityAscii < cols.Count ? cols[idxCityAscii] : string.Empty;
string cityName = string.IsNullOrWhiteSpace(cityAscii) ? rawCity : cityAscii;
string country = idxCountry >= 0 && idxCountry < cols.Count ? cols[idxCountry] : string.Empty;
if (!(idxLat >= 0 && idxLat < cols.Count && idxLng >= 0 && idxLng < cols.Count))
{
continue;
}
if (!double.TryParse(cols[idxLat], NumberStyles.Float, CultureInfo.InvariantCulture, out double lat))
{
continue;
}
if (!double.TryParse(cols[idxLng], NumberStyles.Float, CultureInfo.InvariantCulture, out double lng))
{
continue;
}
if (string.IsNullOrWhiteSpace(cityName))
{
continue;
}
yield return new City(cityName, country, lat, lng);
}
}
/// <summary>
/// Splits a CSV line, handling commas inside quotes and escaped quotes.
/// </summary>
private static List<string> SplitCsvLine(string line)
{
List<string> result = new();
if (string.IsNullOrEmpty(line))
{
result.Add(string.Empty);
return result;
}
bool inQuotes = false;
StringBuilder current = new();
for (int i = 0; i < line.Length; i++)
{
char c = line[i];
if (c == '"')
{
if (inQuotes && i + 1 < line.Length && line[i + 1] == '"')
{
current.Append('"');
i++; // skip escaped quote
}
else
{
inQuotes = !inQuotes;
}
}
else if (c == ',' && !inQuotes)
{
result.Add(current.ToString());
current.Clear();
}
else
{
current.Append(c);
}
}
result.Add(current.ToString());
return result;
}
}
}

Some files were not shown because too many files have changed in this diff Show More