mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-01-20 02:57:03 +01:00
Compare commits
7 Commits
leilzh/fix
...
user/yeela
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8d0c1b312 | ||
|
|
9c58574484 | ||
|
|
08d4689ec5 | ||
|
|
03d1dfca2d | ||
|
|
9086995eeb | ||
|
|
19c9b4e1fd | ||
|
|
d9709b2b91 |
8
.github/actions/spell-check/expect.txt
vendored
8
.github/actions/spell-check/expect.txt
vendored
@@ -365,6 +365,7 @@ DEFAULTICON
|
||||
defaultlib
|
||||
DEFAULTONLY
|
||||
DEFAULTTONEAREST
|
||||
Defaulttonearest
|
||||
DEFAULTTONULL
|
||||
DEFAULTTOPRIMARY
|
||||
DEFERERASE
|
||||
@@ -865,6 +866,7 @@ lastcodeanalysissucceeded
|
||||
LASTEXITCODE
|
||||
LAYOUTRTL
|
||||
lbl
|
||||
Lbuttondown
|
||||
LCh
|
||||
lcid
|
||||
LCIDTo
|
||||
@@ -987,6 +989,7 @@ maxversiontested
|
||||
mber
|
||||
MBM
|
||||
MBR
|
||||
Mbuttondown
|
||||
MDICHILD
|
||||
MDL
|
||||
mdtext
|
||||
@@ -1445,6 +1448,7 @@ RAWINPUTHEADER
|
||||
RAWMODE
|
||||
RAWPATH
|
||||
rbhid
|
||||
Rbuttondown
|
||||
rclsid
|
||||
RCZOOMIT
|
||||
remotedesktop
|
||||
@@ -1480,6 +1484,7 @@ remoteip
|
||||
Removelnk
|
||||
renamable
|
||||
RENAMEONCOLLISION
|
||||
RENDERFULLCONTENT
|
||||
reparented
|
||||
reparenting
|
||||
reportfileaccesses
|
||||
@@ -1746,6 +1751,7 @@ svgz
|
||||
SVSI
|
||||
SWFO
|
||||
SWP
|
||||
Swp
|
||||
SWPNOSIZE
|
||||
SWPNOZORDER
|
||||
SWRESTORE
|
||||
@@ -1765,6 +1771,7 @@ syskeydown
|
||||
SYSKEYUP
|
||||
SYSLIB
|
||||
SYSMENU
|
||||
Sysmenu
|
||||
systemai
|
||||
SYSTEMAPPS
|
||||
SYSTEMMODAL
|
||||
@@ -2089,6 +2096,7 @@ Wwanpp
|
||||
xap
|
||||
XAxis
|
||||
XButton
|
||||
Xbuttondown
|
||||
xclip
|
||||
xcopy
|
||||
XDeployment
|
||||
|
||||
4
.github/copilot-instructions.md
vendored
4
.github/copilot-instructions.md
vendored
@@ -48,9 +48,9 @@ This is the top-level guide for AI changes. Keep edits small, follow existing pa
|
||||
- `doc/devdocs/modules/readme.md`
|
||||
|
||||
# Language style rules
|
||||
- Always enforce repo analyzers: root `.editorconfig` plus any `stylecop.json`.
|
||||
- Always enforce repo analyzers: `src/.editorconfig` plus any `stylecop.json`.
|
||||
- C# code follows StyleCop.Analyzers and Microsoft.CodeAnalysis.NetAnalyzers.
|
||||
- C++ code honors `.clang-format` plus `.clang-tidy` (modernize/cppcoreguidelines/readability).
|
||||
- C++ code honors `src/.clang-format` for formatting.
|
||||
- Markdown files wrap at 80 characters and use ATX headers with fenced code blocks that include language tags.
|
||||
- YAML files indent two spaces and add comments for complex settings while keeping keys clear.
|
||||
- PowerShell scripts use Verb-Noun names and prefer single-quoted literals while documenting parameters and satisfying PSScriptAnalyzer.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
---
|
||||
mode: 'agent'
|
||||
model: Claude Sonnet 4.5
|
||||
agent: 'agent'
|
||||
model: GPT-5.1-Codex-Max
|
||||
description: 'Generate an 80-character git commit title for the local diff.'
|
||||
---
|
||||
|
||||
|
||||
5
.github/prompts/create-pr-summary.prompt.md
vendored
5
.github/prompts/create-pr-summary.prompt.md
vendored
@@ -1,6 +1,5 @@
|
||||
---
|
||||
mode: 'agent'
|
||||
model: Claude Sonnet 4.5
|
||||
agent: 'agent'
|
||||
model: GPT-5.1-Codex-Max
|
||||
description: 'Generate a PowerToys-ready pull request description from the local diff.'
|
||||
---
|
||||
|
||||
|
||||
7
.github/prompts/fix-issue.prompt.md
vendored
7
.github/prompts/fix-issue.prompt.md
vendored
@@ -1,7 +1,6 @@
|
||||
---
|
||||
mode: 'agent'
|
||||
model: GPT-5-Codex (Preview)
|
||||
description: " Execute the fix for a GitHub issue using the previously generated implementation plan. Apply code & tests directly in the repo. Output only a PR description (and optional manual steps)."
|
||||
agent: 'agent'
|
||||
model: GPT-5.1-Codex-Max
|
||||
description: "Execute the fix for a GitHub issue using the previously generated implementation plan. Apply code & tests directly in the repo. Output only a PR description (and optional manual steps)."
|
||||
---
|
||||
|
||||
# DEPENDENCY
|
||||
|
||||
5
.github/prompts/fix-spelling.prompt.md
vendored
5
.github/prompts/fix-spelling.prompt.md
vendored
@@ -1,6 +1,5 @@
|
||||
---
|
||||
mode: 'agent'
|
||||
model: GPT-5-Codex (Preview)
|
||||
agent: 'agent'
|
||||
model: GPT-5.1-Codex-Max
|
||||
description: 'Resolve Code scanning / check-spelling comments on the active PR.'
|
||||
---
|
||||
|
||||
|
||||
13
.github/prompts/review-issue.prompt.md
vendored
13
.github/prompts/review-issue.prompt.md
vendored
@@ -1,7 +1,6 @@
|
||||
---
|
||||
mode: 'agent'
|
||||
model: Claude Sonnet 4.5
|
||||
description: "You are github issue review and planning expertise, Score (0–100) and write one Implementation Plan. Outputs: overview.md, implementation-plan.md."
|
||||
agent: 'agent'
|
||||
model: GPT-5.1-Codex-Max
|
||||
description: "You are a GitHub issue review and planning expert; score (0-100) and write one implementation plan. Outputs: overview.md, implementation-plan.md."
|
||||
---
|
||||
|
||||
# GOAL
|
||||
@@ -10,11 +9,11 @@ For **#{{issue_number}}** produce:
|
||||
2) `Generated Files/issueReview/{{issue_number}}/implementation-plan.md`
|
||||
|
||||
## Inputs
|
||||
figure out from the prompt on the
|
||||
Figure out required inputs {{issue_number}} from the invocation context; if anything is missing, ask for the value or note it as a gap.
|
||||
|
||||
# CONTEXT (brief)
|
||||
Ground evidence using `gh issue view {{issue_number}} --json number,title,body,author,createdAt,updatedAt,state,labels,milestone,reactions,comments,linkedPullRequests`, and download the image for understand the context of the issue more.
|
||||
Locate source code in current workspace, but also free feel to use via `rg`/`git grep`. Link related issues/PRs.
|
||||
Ground evidence using `gh issue view {{issue_number}} --json number,title,body,author,createdAt,updatedAt,state,labels,milestone,reactions,comments,linkedPullRequests`, and download images to better understand the issue context.
|
||||
Locate source code in the current workspace; feel free to use `rg`/`git grep`. Link related issues and PRs.
|
||||
|
||||
# OVERVIEW.MD
|
||||
## Summary
|
||||
|
||||
5
.github/prompts/review-pr.prompt.md
vendored
5
.github/prompts/review-pr.prompt.md
vendored
@@ -1,6 +1,5 @@
|
||||
---
|
||||
mode: 'agent'
|
||||
model: Claude Sonnet 4.5
|
||||
agent: 'agent'
|
||||
model: GPT-5.1-Codex-Max
|
||||
description: "gh-driven PR review; per-step Markdown + machine-readable outputs"
|
||||
---
|
||||
|
||||
|
||||
@@ -68,14 +68,13 @@ jobs:
|
||||
|
||||
- template: .\steps-restore-nuget.yml
|
||||
|
||||
- task: NuGetCommand@2
|
||||
- task: MSBuild@1
|
||||
displayName: Restore solution-level NuGet packages
|
||||
inputs:
|
||||
command: restore
|
||||
feedsToUse: config
|
||||
configPath: nuget.config
|
||||
restoreSolution: PowerToys.slnx
|
||||
restoreDirectory: '$(Build.SourcesDirectory)\packages'
|
||||
solution: PowerToys.slnx
|
||||
msbuildArguments: '/t:restore /p:RestorePackagesConfig=true'
|
||||
platform: $(BuildPlatform)
|
||||
configuration: $(BuildConfiguration)
|
||||
|
||||
# Build all UI test projects if no specific modules are specified
|
||||
- ${{ if eq(length(parameters.uiTestModules), 0) }}:
|
||||
|
||||
106
.vscode/tasks.json
vendored
Normal file
106
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"windows": {
|
||||
"options": {
|
||||
"shell": {
|
||||
"executable": "cmd.exe",
|
||||
"args": ["/d", "/c"]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"inputs": [
|
||||
{
|
||||
"id": "config",
|
||||
"type": "pickString",
|
||||
"description": "Configuration",
|
||||
"options": ["Debug", "Release"],
|
||||
"default": "Debug"
|
||||
},
|
||||
{
|
||||
"id": "platform",
|
||||
"type": "pickString",
|
||||
"description": "Platform (leave empty to auto-detect host platform)",
|
||||
"options": ["", "X64", "ARM64"],
|
||||
"default": "X64"
|
||||
},
|
||||
{
|
||||
"id": "msbuildExtra",
|
||||
"type": "promptString",
|
||||
"description": "Extra MSBuild args (optional). Example: /p:CIBuild=true /m",
|
||||
"default": ""
|
||||
}
|
||||
],
|
||||
|
||||
"tasks": [
|
||||
{
|
||||
"label": "PT: Build (quick)",
|
||||
"type": "shell",
|
||||
"command": "\"${workspaceFolder}\\tools\\build\\build.cmd\"",
|
||||
"args": [
|
||||
"-Path",
|
||||
"${fileDirname}"
|
||||
],
|
||||
"group": { "kind": "build", "isDefault": true },
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "dedicated",
|
||||
"clear": true
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
|
||||
{
|
||||
"label": "PT: Build (with options)",
|
||||
"type": "shell",
|
||||
"command": "\"${workspaceFolder}\\tools\\build\\build.cmd\"",
|
||||
"args": [
|
||||
"-Path",
|
||||
"${fileDirname}",
|
||||
"-Platform",
|
||||
"${input:platform}",
|
||||
"-Configuration",
|
||||
"${input:config}",
|
||||
"${input:msbuildExtra}"
|
||||
],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "dedicated",
|
||||
"clear": true
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
|
||||
{
|
||||
"label": "PT: Build Essentials (quick)",
|
||||
"type": "shell",
|
||||
"command": "\"${workspaceFolder}\\tools\\build\\build-essentials.cmd\"",
|
||||
"args": [],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "dedicated",
|
||||
"clear": true
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
|
||||
{
|
||||
"label": "PT: Build Essentials (with options)",
|
||||
"type": "shell",
|
||||
"command": "\"${workspaceFolder}\\tools\\build\\build-essentials.cmd\"",
|
||||
"args": [
|
||||
"-Platform",
|
||||
"${input:platform}",
|
||||
"-Configuration",
|
||||
"${input:config}",
|
||||
"${input:msbuildExtra}"
|
||||
],
|
||||
"presentation": {
|
||||
"reveal": "always",
|
||||
"panel": "dedicated",
|
||||
"clear": true
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
||||
36
COMMUNITY.md
36
COMMUNITY.md
@@ -6,9 +6,6 @@ Names are in alphabetical order based on first name.
|
||||
|
||||
## High impact community members
|
||||
|
||||
### [@Noraa-Junker](https://github.com/Noraa-Junker) - [Noraa Junker](https://noraajunker.ch)
|
||||
Noraa has helped triaging, discussing, and creating a substantial number of issues and contributed features/fixes. Noraa was the primary person for helping build the File Explorer preview pane handler for developer files.
|
||||
|
||||
### [@cgaarden](https://github.com/cgaarden) - [Christian Gaarden Gaardmark](https://www.onegreatworld.com)
|
||||
Christian contributed New+ utility
|
||||
|
||||
@@ -42,6 +39,12 @@ Jay has helped triaging, discussing, creating a substantial number of issues and
|
||||
### [@jefflord](https://github.com/Jjefflord) - Jeff Lord
|
||||
Jeff added in multiple new features into Keyboard manager, such as key chord support and launching apps. He also contributed multiple features/fixes to PowerToys.
|
||||
|
||||
### [@snickler](https://github.com/snickler) - [Jeremy Sinclair](http://sinclairinat0r.com)
|
||||
Jeremy has helped drive large sums of the ARM64 support inside PowerToys
|
||||
|
||||
### [@jiripolasek](https://github.com/jiripolasek) - [Jiří Polášek](https://github.com/jiripolasek)
|
||||
Jiří has contributed a massive number of features and improvements to Command Palette, including drag & drop support, custom themes, Web Search enhancements, Remote Desktop extension fixes, and many UX improvements.
|
||||
|
||||
### [@TheJoeFin](https://github.com/TheJoeFin) - [Joe Finney](https://joefinapps.com)
|
||||
Joe has helped triaging, discussing, issues as well as fixing bugs and building features for Text Extractor.
|
||||
|
||||
@@ -57,6 +60,9 @@ Color Picker is from Martin.
|
||||
### [@mikeclayton](https://github.com/mikeclayton) - [Michael Clayton](https://michael-clayton.com)
|
||||
Michael contributed the [initial version](https://github.com/microsoft/PowerToys/issues/23216) of the Mouse Jump tool and [a number of updates](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+author%3Amikeclayton) based on his FancyMouse utility.
|
||||
|
||||
### [@Noraa-Junker](https://github.com/Noraa-Junker) - [Noraa Junker](https://noraajunker.ch)
|
||||
Noraa has helped triaging, discussing, and creating a substantial number of issues and contributed features/fixes. Noraa was the primary person for helping build the File Explorer preview pane handler for developer files.
|
||||
|
||||
### [@pedrolamas](https://github.com/pedrolamas/) - Pedro Lamas
|
||||
Pedro helped create the thumbnail and File Explorer previewers for 3D files like STL and GCode. If you like 3D printing, these are very helpful.
|
||||
|
||||
@@ -69,15 +75,12 @@ Rafael has helped do the [upgrade from CppWinRT 1.x to 2.0](https://github.com/m
|
||||
### [@royvou](https://github.com/royvou)
|
||||
Roy has helped out contributing multiple features to PowerToys Run
|
||||
|
||||
### [@snickler](https://github.com/snickler) - [Jeremy Sinclair](http://sinclairinat0r.com)
|
||||
Jeremy has helped drive large sums of the ARM64 support inside PowerToys
|
||||
### [@ThiefZero](https://github.com/ThiefZero)
|
||||
ThiefZero has helped out contributing a features to PowerToys Run such as the unit converter plugin
|
||||
|
||||
### [@TobiasSekan](https://github.com/TobiasSekan) - Tobias Sekan
|
||||
Tobias Sekan has helped out contributing features to PowerToys Run such as Settings plugin, Registry plugin
|
||||
|
||||
### [@ThiefZero](https://github.com/ThiefZero)
|
||||
ThiefZero has helped out contributing a features to PowerToys Run such as the unit converter plugin
|
||||
|
||||
## Open source projects
|
||||
|
||||
As PowerToys creates new utilities, some will be based off existing technology. We'll continue to do our best to contribute back to these projects but their efforts were the base of some of our projects. We want to be sure their work is directly recognized.
|
||||
@@ -187,18 +190,10 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter
|
||||
- [@niels9001](https://github.com/niels9001/) - Niels Laute - Product Manager
|
||||
- [@dhowett](https://github.com/dhowett) - Dustin Howett - Dev Lead
|
||||
- [@yeelam-gordon](https://github.com/yeelam-gordon) - Gordon Lam - Dev Lead
|
||||
- [@jamrobot](https://github.com/jamrobot) - Jerry Xu - Dev Lead
|
||||
- [@lei9444](https://github.com/lei9444) - Leilei Zhang - Dev
|
||||
- [@shuaiyuanxx](https://github.com/shuaiyuanxx) - Shawn Yuan - Dev
|
||||
- [@moooyo](https://github.com/moooyo) - Yu Leng - Dev
|
||||
- [@haoliuu](https://github.com/haoliuu) - Hao Liu - Dev
|
||||
- [@chenmy77](https://github.com/chenmy77) - Mengyuan Chen - Dev
|
||||
- [@chemwolf6922](https://github.com/chemwolf6922) - Feng Wang - Dev
|
||||
- [@yaqingmi](https://github.com/yaqingmi) - Yaqing Mi - Dev
|
||||
- [@zhaoqpcn](https://github.com/zhaoqpcn) - Qingpeng Zhao - Dev
|
||||
- [@urnotdfs](https://github.com/urnotdfs) - Xiaofeng Wang - Dev
|
||||
- [@zhaopy536](https://github.com/zhaopy536) - Peiyao Zhao - Dev
|
||||
- [@wang563681252](https://github.com/wang563681252) - Zhaopeng Wang - Dev
|
||||
- [@vanzue](https://github.com/vanzue) - Kai Tao - Dev
|
||||
- [@zadjii-msft](https://github.com/zadjii-msft) - Mike Griese - Dev
|
||||
- [@khmyznikov](https://github.com/khmyznikov) - Gleb Khmyznikov - Dev
|
||||
@@ -229,3 +224,12 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter
|
||||
- [@SeraphimaZykova](https://github.com/SeraphimaZykova) - Seraphima Zykova - Dev
|
||||
- [@stefansjfw](https://github.com/stefansjfw) - Stefan Markovic - Dev
|
||||
- [@jaimecbernardo](https://github.com/jaimecbernardo) - Jaime Bernardo - Dev Lead
|
||||
- [@haoliuu](https://github.com/haoliuu) - Hao Liu - Dev
|
||||
- [@chenmy77](https://github.com/chenmy77) - Mengyuan Chen - Dev
|
||||
- [@chemwolf6922](https://github.com/chemwolf6922) - Feng Wang - Dev
|
||||
- [@yaqingmi](https://github.com/yaqingmi) - Yaqing Mi - Dev
|
||||
- [@zhaoqpcn](https://github.com/zhaoqpcn) - Qingpeng Zhao - Dev
|
||||
- [@urnotdfs](https://github.com/urnotdfs) - Xiaofeng Wang - Dev
|
||||
- [@zhaopy536](https://github.com/zhaopy536) - Peiyao Zhao - Dev
|
||||
- [@wang563681252](https://github.com/wang563681252) - Zhaopeng Wang - Dev
|
||||
- [@jamrobot](https://github.com/jamrobot) - Jerry Xu - Dev Lead
|
||||
|
||||
@@ -1005,6 +1005,14 @@
|
||||
<Project Path="src/modules/ZoomIt/ZoomItSettingsInterop/ZoomItSettingsInterop.vcxproj" Id="ca7d8106-30b9-4aec-9d05-b69b31b8c461" />
|
||||
</Folder>
|
||||
<Folder Name="/settings-ui/">
|
||||
<Project Path="src/settings-ui/QuickAccess.UI/PowerToys.QuickAccess.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/settings-ui/Settings.UI.Controls/Settings.UI.Controls.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
</Project>
|
||||
<Project Path="src/settings-ui/Settings.UI.Library/Settings.UI.Library.csproj">
|
||||
<Platform Solution="*|ARM64" Project="ARM64" />
|
||||
<Platform Solution="*|x64" Project="x64" />
|
||||
|
||||
@@ -20,6 +20,9 @@ Creates a window showing the selected area of the original window. Changes in th
|
||||
### Reparent Mode
|
||||
Creates a window that replaces the original window, showing only the selected area. The application is controlled through the cropped window.
|
||||
|
||||
### Screenshot Mode
|
||||
Creates a window showing a freezed snapshot of the original window.
|
||||
|
||||
## Code Structure
|
||||
|
||||
### Project Layout
|
||||
@@ -30,6 +33,7 @@ The Crop and Lock module is part of the PowerToys solution. All the logic-relate
|
||||
- **OverlayWindow.cpp**: Thumbnail module type's window concrete implementation.
|
||||
- **ReparentCropAndLockWindow.cpp**: Defines the UI for the reparent mode.
|
||||
- **ChildWindow.cpp**: Reparent module type's window concrete implementation.
|
||||
- **ScreenshotCropAndLockWindow.cpp**: Defines the UI for the screenshot mode.
|
||||
|
||||
## Known Issues
|
||||
|
||||
|
||||
@@ -36,5 +36,6 @@ namespace ManagedCommon
|
||||
PowerOCR,
|
||||
Workspaces,
|
||||
ZoomIt,
|
||||
GeneralSettings,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +119,16 @@ namespace PowerToysSettings
|
||||
class HotkeyObject
|
||||
{
|
||||
public:
|
||||
HotkeyObject() :
|
||||
m_json(json::JsonObject())
|
||||
{
|
||||
m_json.SetNamedValue(L"win", json::value(false));
|
||||
m_json.SetNamedValue(L"ctrl", json::value(false));
|
||||
m_json.SetNamedValue(L"alt", json::value(false));
|
||||
m_json.SetNamedValue(L"shift", json::value(false));
|
||||
m_json.SetNamedValue(L"code", json::value(0));
|
||||
m_json.SetNamedValue(L"key", json::value(L""));
|
||||
}
|
||||
static HotkeyObject from_json(json::JsonObject json)
|
||||
{
|
||||
return HotkeyObject(std::move(json));
|
||||
|
||||
@@ -223,6 +223,10 @@ namespace winrt::PowerToys::Interop::implementation
|
||||
{
|
||||
return CommonSharedConstants::CROP_AND_LOCK_REPARENT_EVENT;
|
||||
}
|
||||
hstring Constants::CropAndLockScreenshotEvent()
|
||||
{
|
||||
return CommonSharedConstants::CROP_AND_LOCK_SCREENSHOT_EVENT;
|
||||
}
|
||||
hstring Constants::ShowEnvironmentVariablesSharedEvent()
|
||||
{
|
||||
return CommonSharedConstants::SHOW_ENVIRONMENT_VARIABLES_EVENT;
|
||||
|
||||
@@ -59,6 +59,7 @@ namespace winrt::PowerToys::Interop::implementation
|
||||
static hstring TerminateHostsSharedEvent();
|
||||
static hstring CropAndLockThumbnailEvent();
|
||||
static hstring CropAndLockReparentEvent();
|
||||
static hstring CropAndLockScreenshotEvent();
|
||||
static hstring ShowEnvironmentVariablesSharedEvent();
|
||||
static hstring ShowEnvironmentVariablesAdminSharedEvent();
|
||||
static hstring WorkspacesLaunchEditorEvent();
|
||||
|
||||
@@ -56,6 +56,7 @@ namespace PowerToys
|
||||
static String TerminateHostsSharedEvent();
|
||||
static String CropAndLockThumbnailEvent();
|
||||
static String CropAndLockReparentEvent();
|
||||
static String CropAndLockScreenshotEvent();
|
||||
static String ShowEnvironmentVariablesSharedEvent();
|
||||
static String ShowEnvironmentVariablesAdminSharedEvent();
|
||||
static String WorkspacesLaunchEditorEvent();
|
||||
|
||||
@@ -132,6 +132,7 @@ namespace CommonSharedConstants
|
||||
// Path to the events used by CropAndLock
|
||||
const wchar_t CROP_AND_LOCK_REPARENT_EVENT[] = L"Local\\PowerToysCropAndLockReparentEvent-6060860a-76a1-44e8-8d0e-6355785e9c36";
|
||||
const wchar_t CROP_AND_LOCK_THUMBNAIL_EVENT[] = L"Local\\PowerToysCropAndLockThumbnailEvent-1637be50-da72-46b2-9220-b32b206b2434";
|
||||
const wchar_t CROP_AND_LOCK_SCREENSHOT_EVENT[] = L"Local\\PowerToysCropAndLockScreenshotEvent-ff077ab2-8360-4bd1-864a-637389d35593";
|
||||
const wchar_t CROP_AND_LOCK_EXIT_EVENT[] = L"Local\\PowerToysCropAndLockExitEvent-d995d409-7b70-482b-bad6-e7c8666f375a";
|
||||
|
||||
// Path to the events used by EnvironmentVariables
|
||||
|
||||
@@ -112,6 +112,7 @@
|
||||
<ClCompile Include="ChildWindow.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="ReparentCropAndLockWindow.cpp" />
|
||||
<ClCompile Include="ScreenshotCropAndLockWindow.cpp" />
|
||||
<ClCompile Include="ThumbnailCropAndLockWindow.cpp" />
|
||||
<ClCompile Include="OverlayWindow.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
@@ -126,6 +127,7 @@
|
||||
<ClInclude Include="DisplaysUtil.h" />
|
||||
<ClInclude Include="ModuleConstants.h" />
|
||||
<ClInclude Include="ReparentCropAndLockWindow.h" />
|
||||
<ClInclude Include="ScreenshotCropAndLockWindow.h" />
|
||||
<ClInclude Include="ThumbnailCropAndLockWindow.h" />
|
||||
<ClInclude Include="SettingsWindow.h" />
|
||||
<ClInclude Include="OverlayWindow.h" />
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<ClCompile Include="ReparentCropAndLockWindow.cpp" />
|
||||
<ClCompile Include="ChildWindow.cpp" />
|
||||
<ClCompile Include="trace.cpp" />
|
||||
<ClCompile Include="ScreenshotCropAndLockWindow.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
@@ -28,6 +29,7 @@
|
||||
<ClInclude Include="trace.h" />
|
||||
<ClInclude Include="ModuleConstants.h" />
|
||||
<ClInclude Include="DispatcherQueue.desktop.interop.h" />
|
||||
<ClInclude Include="ScreenshotCropAndLockWindow.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="CropAndLock.rc" />
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
#include "pch.h"
|
||||
#include "ScreenshotCropAndLockWindow.h"
|
||||
|
||||
const std::wstring ScreenshotCropAndLockWindow::ClassName = L"CropAndLock.ScreenshotCropAndLockWindow";
|
||||
std::once_flag ScreenshotCropAndLockWindowClassRegistration;
|
||||
|
||||
void ScreenshotCropAndLockWindow::RegisterWindowClass()
|
||||
{
|
||||
auto instance = winrt::check_pointer(GetModuleHandleW(nullptr));
|
||||
WNDCLASSEXW wcex = {};
|
||||
wcex.cbSize = sizeof(wcex);
|
||||
wcex.style = CS_HREDRAW | CS_VREDRAW;
|
||||
wcex.lpfnWndProc = WndProc;
|
||||
wcex.hInstance = instance;
|
||||
wcex.hIcon = LoadIconW(instance, IDI_APPLICATION);
|
||||
wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW);
|
||||
wcex.hbrBackground = static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
|
||||
wcex.lpszClassName = ClassName.c_str();
|
||||
wcex.hIconSm = LoadIconW(wcex.hInstance, IDI_APPLICATION);
|
||||
winrt::check_bool(RegisterClassExW(&wcex));
|
||||
}
|
||||
|
||||
ScreenshotCropAndLockWindow::ScreenshotCropAndLockWindow(std::wstring const& titleString, int width, int height)
|
||||
{
|
||||
auto instance = winrt::check_pointer(GetModuleHandleW(nullptr));
|
||||
|
||||
std::call_once(ScreenshotCropAndLockWindowClassRegistration, []() { RegisterWindowClass(); });
|
||||
|
||||
auto exStyle = 0;
|
||||
auto style = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN;
|
||||
|
||||
RECT rect = { 0, 0, width, height };
|
||||
winrt::check_bool(AdjustWindowRectEx(&rect, style, false, exStyle));
|
||||
auto adjustedWidth = rect.right - rect.left;
|
||||
auto adjustedHeight = rect.bottom - rect.top;
|
||||
|
||||
winrt::check_bool(CreateWindowExW(exStyle, ClassName.c_str(), titleString.c_str(), style, CW_USEDEFAULT, CW_USEDEFAULT, adjustedWidth, adjustedHeight, nullptr, nullptr, instance, this));
|
||||
WINRT_ASSERT(m_window);
|
||||
}
|
||||
|
||||
ScreenshotCropAndLockWindow::~ScreenshotCropAndLockWindow()
|
||||
{
|
||||
DestroyWindow(m_window);
|
||||
}
|
||||
|
||||
LRESULT ScreenshotCropAndLockWindow::MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case WM_DESTROY:
|
||||
if (m_closedCallback != nullptr && !m_destroyed)
|
||||
{
|
||||
m_destroyed = true;
|
||||
m_closedCallback(m_window);
|
||||
}
|
||||
break;
|
||||
case WM_PAINT:
|
||||
if (m_captured && m_bitmap)
|
||||
{
|
||||
PAINTSTRUCT ps;
|
||||
HDC hdc = BeginPaint(m_window, &ps);
|
||||
HDC memDC = CreateCompatibleDC(hdc);
|
||||
SelectObject(memDC, m_bitmap.get());
|
||||
|
||||
RECT clientRect = {};
|
||||
GetClientRect(m_window, &clientRect);
|
||||
int clientWidth = clientRect.right - clientRect.left;
|
||||
int clientHeight = clientRect.bottom - clientRect.top;
|
||||
|
||||
int srcWidth = m_destRect.right - m_destRect.left;
|
||||
int srcHeight = m_destRect.bottom - m_destRect.top;
|
||||
|
||||
float srcAspect = static_cast<float>(srcWidth) / srcHeight;
|
||||
float dstAspect = static_cast<float>(clientWidth) / clientHeight;
|
||||
|
||||
int drawWidth = clientWidth;
|
||||
int drawHeight = static_cast<int>(clientWidth / srcAspect);
|
||||
if (dstAspect > srcAspect)
|
||||
{
|
||||
drawHeight = clientHeight;
|
||||
drawWidth = static_cast<int>(clientHeight * srcAspect);
|
||||
}
|
||||
|
||||
int offsetX = (clientWidth - drawWidth) / 2;
|
||||
int offsetY = (clientHeight - drawHeight) / 2;
|
||||
|
||||
SetStretchBltMode(hdc, HALFTONE);
|
||||
StretchBlt(hdc, offsetX, offsetY, drawWidth, drawHeight, memDC, 0, 0, srcWidth, srcHeight, SRCCOPY);
|
||||
DeleteDC(memDC);
|
||||
EndPaint(m_window, &ps);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return base_type::MessageHandler(message, wparam, lparam);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ScreenshotCropAndLockWindow::CropAndLock(HWND windowToCrop, RECT cropRect)
|
||||
{
|
||||
if (m_captured)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get full window bounds
|
||||
RECT windowRect{};
|
||||
winrt::check_hresult(DwmGetWindowAttribute(
|
||||
windowToCrop,
|
||||
DWMWA_EXTENDED_FRAME_BOUNDS,
|
||||
&windowRect,
|
||||
sizeof(windowRect)));
|
||||
|
||||
RECT clientRect = ClientAreaInScreenSpace(windowToCrop);
|
||||
auto offsetX = clientRect.left - windowRect.left;
|
||||
auto offsetY = clientRect.top - windowRect.top;
|
||||
|
||||
m_sourceRect = {
|
||||
cropRect.left + offsetX,
|
||||
cropRect.top + offsetY,
|
||||
cropRect.right + offsetX,
|
||||
cropRect.bottom + offsetY
|
||||
};
|
||||
|
||||
int fullWidth = windowRect.right - windowRect.left;
|
||||
int fullHeight = windowRect.bottom - windowRect.top;
|
||||
|
||||
HDC fullDC = CreateCompatibleDC(nullptr);
|
||||
HDC screenDC = GetDC(nullptr);
|
||||
HBITMAP fullBitmap = CreateCompatibleBitmap(screenDC, fullWidth, fullHeight);
|
||||
HGDIOBJ oldFullBitmap = SelectObject(fullDC, fullBitmap);
|
||||
|
||||
// Capture full window
|
||||
winrt::check_bool(PrintWindow(windowToCrop, fullDC, PW_RENDERFULLCONTENT));
|
||||
|
||||
|
||||
// Crop
|
||||
int cropWidth = m_sourceRect.right - m_sourceRect.left;
|
||||
int cropHeight = m_sourceRect.bottom - m_sourceRect.top;
|
||||
|
||||
HDC cropDC = CreateCompatibleDC(nullptr);
|
||||
HBITMAP cropBitmap = CreateCompatibleBitmap(screenDC, cropWidth, cropHeight);
|
||||
HGDIOBJ oldCropBitmap = SelectObject(cropDC, cropBitmap);
|
||||
ReleaseDC(nullptr, screenDC);
|
||||
|
||||
BitBlt(
|
||||
cropDC,
|
||||
0,
|
||||
0,
|
||||
cropWidth,
|
||||
cropHeight,
|
||||
fullDC,
|
||||
m_sourceRect.left,
|
||||
m_sourceRect.top,
|
||||
SRCCOPY);
|
||||
|
||||
SelectObject(fullDC, oldFullBitmap);
|
||||
DeleteObject(fullBitmap);
|
||||
DeleteDC(fullDC);
|
||||
|
||||
SelectObject(cropDC, oldCropBitmap);
|
||||
DeleteDC(cropDC);
|
||||
m_bitmap.reset(cropBitmap);
|
||||
|
||||
// Resize our window
|
||||
RECT dest{ 0, 0, cropWidth, cropHeight };
|
||||
LONG_PTR exStyle = GetWindowLongPtrW(m_window, GWL_EXSTYLE);
|
||||
LONG_PTR style = GetWindowLongPtrW(m_window, GWL_STYLE);
|
||||
|
||||
winrt::check_bool(AdjustWindowRectEx(&dest, static_cast<DWORD>(style), FALSE, static_cast<DWORD>(exStyle)));
|
||||
|
||||
winrt::check_bool(SetWindowPos(
|
||||
m_window, HWND_TOPMOST, 0, 0, dest.right - dest.left, dest.bottom - dest.top, SWP_NOMOVE | SWP_SHOWWINDOW));
|
||||
|
||||
m_destRect = { 0, 0, cropWidth, cropHeight };
|
||||
m_captured = true;
|
||||
InvalidateRect(m_window, nullptr, FALSE);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <robmikh.common/DesktopWindow.h>
|
||||
#include "CropAndLockWindow.h"
|
||||
|
||||
struct ScreenshotCropAndLockWindow : robmikh::common::desktop::DesktopWindow<ScreenshotCropAndLockWindow>, CropAndLockWindow
|
||||
{
|
||||
static const std::wstring ClassName;
|
||||
ScreenshotCropAndLockWindow(std::wstring const& titleString, int width, int height);
|
||||
~ScreenshotCropAndLockWindow() override;
|
||||
LRESULT MessageHandler(UINT const message, WPARAM const wparam, LPARAM const lparam);
|
||||
|
||||
HWND Handle() override { return m_window; }
|
||||
void CropAndLock(HWND windowToCrop, RECT cropRect) override;
|
||||
void OnClosed(std::function<void(HWND)> callback) override { m_closedCallback = callback; }
|
||||
|
||||
private:
|
||||
static void RegisterWindowClass();
|
||||
|
||||
private:
|
||||
std::unique_ptr<void, decltype(&DeleteObject)> m_bitmap{ nullptr, &DeleteObject };
|
||||
RECT m_destRect = {};
|
||||
RECT m_sourceRect = {};
|
||||
|
||||
bool m_captured = false;
|
||||
bool m_destroyed = false;
|
||||
std::function<void(HWND)> m_closedCallback;
|
||||
};
|
||||
@@ -4,4 +4,5 @@ enum class CropAndLockType
|
||||
{
|
||||
Reparent,
|
||||
Thumbnail,
|
||||
Screenshot,
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "SettingsWindow.h"
|
||||
#include "OverlayWindow.h"
|
||||
#include "CropAndLockWindow.h"
|
||||
#include "ScreenshotCropAndLockWindow.h"
|
||||
#include "ThumbnailCropAndLockWindow.h"
|
||||
#include "ReparentCropAndLockWindow.h"
|
||||
#include "ModuleConstants.h"
|
||||
@@ -133,6 +134,7 @@ int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PWSTR lpCmdLine, _I
|
||||
// Handles and thread for the events sent from runner
|
||||
HANDLE m_reparent_event_handle;
|
||||
HANDLE m_thumbnail_event_handle;
|
||||
HANDLE m_screenshot_event_handle;
|
||||
HANDLE m_exit_event_handle;
|
||||
std::thread m_event_triggers_thread;
|
||||
|
||||
@@ -181,6 +183,11 @@ int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PWSTR lpCmdLine, _I
|
||||
Logger::trace(L"Creating a thumbnail window");
|
||||
Trace::CropAndLock::CreateThumbnailWindow();
|
||||
break;
|
||||
case CropAndLockType::Screenshot:
|
||||
croppedWindow = std::make_shared<ScreenshotCropAndLockWindow>(title, 800, 600);
|
||||
Logger::trace(L"Creating a screenshot window");
|
||||
Trace::CropAndLock::CreateScreenshotWindow();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
@@ -215,8 +222,9 @@ int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PWSTR lpCmdLine, _I
|
||||
// Start a thread to listen on the events.
|
||||
m_reparent_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::CROP_AND_LOCK_REPARENT_EVENT);
|
||||
m_thumbnail_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::CROP_AND_LOCK_THUMBNAIL_EVENT);
|
||||
m_screenshot_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::CROP_AND_LOCK_SCREENSHOT_EVENT);
|
||||
m_exit_event_handle = CreateEventW(nullptr, false, false, CommonSharedConstants::CROP_AND_LOCK_EXIT_EVENT);
|
||||
if (!m_reparent_event_handle || !m_thumbnail_event_handle || !m_exit_event_handle)
|
||||
if (!m_reparent_event_handle || !m_thumbnail_event_handle || !m_screenshot_event_handle || !m_exit_event_handle)
|
||||
{
|
||||
Logger::warn(L"Failed to create events. {}", get_last_error_or_default(GetLastError()));
|
||||
return 1;
|
||||
@@ -224,10 +232,10 @@ int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PWSTR lpCmdLine, _I
|
||||
|
||||
m_event_triggers_thread = std::thread([&]() {
|
||||
MSG msg;
|
||||
HANDLE event_handles[3] = { m_reparent_event_handle, m_thumbnail_event_handle, m_exit_event_handle };
|
||||
HANDLE event_handles[4] = { m_reparent_event_handle, m_thumbnail_event_handle, m_screenshot_event_handle, m_exit_event_handle };
|
||||
while (m_running)
|
||||
{
|
||||
DWORD dwEvt = MsgWaitForMultipleObjects(3, event_handles, false, INFINITE, QS_ALLINPUT);
|
||||
DWORD dwEvt = MsgWaitForMultipleObjects(4, event_handles, false, INFINITE, QS_ALLINPUT);
|
||||
if (!m_running)
|
||||
{
|
||||
break;
|
||||
@@ -259,13 +267,25 @@ int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PWSTR lpCmdLine, _I
|
||||
break;
|
||||
}
|
||||
case WAIT_OBJECT_0 + 2:
|
||||
{
|
||||
// Screenshot Event
|
||||
bool enqueueSucceeded = controller.DispatcherQueue().TryEnqueue([&]() {
|
||||
ProcessCommand(CropAndLockType::Screenshot);
|
||||
});
|
||||
if (!enqueueSucceeded)
|
||||
{
|
||||
Logger::error("Couldn't enqueue message to screenshot a window.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WAIT_OBJECT_0 + 3:
|
||||
{
|
||||
// Exit Event
|
||||
Logger::trace(L"Received an exit event.");
|
||||
PostThreadMessage(mainThreadId, WM_QUIT, 0, 0);
|
||||
break;
|
||||
}
|
||||
case WAIT_OBJECT_0 + 3:
|
||||
case WAIT_OBJECT_0 + 4:
|
||||
if (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE))
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
@@ -295,6 +315,7 @@ int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PWSTR lpCmdLine, _I
|
||||
SetEvent(m_reparent_event_handle);
|
||||
CloseHandle(m_reparent_event_handle);
|
||||
CloseHandle(m_thumbnail_event_handle);
|
||||
CloseHandle(m_screenshot_event_handle);
|
||||
CloseHandle(m_exit_event_handle);
|
||||
m_event_triggers_thread.join();
|
||||
|
||||
|
||||
@@ -41,6 +41,15 @@ void Trace::CropAndLock::ActivateThumbnail() noexcept
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
}
|
||||
|
||||
void Trace::CropAndLock::ActivateScreenshot() noexcept
|
||||
{
|
||||
TraceLoggingWriteWrapper(
|
||||
g_hProvider,
|
||||
"CropAndLock_ActivateScreenshot",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
}
|
||||
|
||||
void Trace::CropAndLock::CreateReparentWindow() noexcept
|
||||
{
|
||||
TraceLoggingWriteWrapper(
|
||||
@@ -59,8 +68,17 @@ void Trace::CropAndLock::CreateThumbnailWindow() noexcept
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
}
|
||||
|
||||
void Trace::CropAndLock::CreateScreenshotWindow() noexcept
|
||||
{
|
||||
TraceLoggingWriteWrapper(
|
||||
g_hProvider,
|
||||
"CropAndLock_CreateScreenshotWindow",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
}
|
||||
|
||||
// Event to send settings telemetry.
|
||||
void Trace::CropAndLock::SettingsTelemetry(PowertoyModuleIface::Hotkey& reparentHotkey, PowertoyModuleIface::Hotkey& thumbnailHotkey) noexcept
|
||||
void Trace::CropAndLock::SettingsTelemetry(PowertoyModuleIface::Hotkey& reparentHotkey, PowertoyModuleIface::Hotkey& thumbnailHotkey, PowertoyModuleIface::Hotkey& screenshotHotkey) noexcept
|
||||
{
|
||||
std::wstring hotKeyStrReparent =
|
||||
std::wstring(reparentHotkey.win ? L"Win + " : L"") +
|
||||
@@ -76,11 +94,19 @@ void Trace::CropAndLock::SettingsTelemetry(PowertoyModuleIface::Hotkey& reparent
|
||||
std::wstring(thumbnailHotkey.alt ? L"Alt + " : L"") +
|
||||
std::wstring(L"VK ") + std::to_wstring(thumbnailHotkey.key);
|
||||
|
||||
std::wstring hotKeyStrScreenshot =
|
||||
std::wstring(screenshotHotkey.win ? L"Win + " : L"") +
|
||||
std::wstring(screenshotHotkey.ctrl ? L"Ctrl + " : L"") +
|
||||
std::wstring(screenshotHotkey.shift ? L"Shift + " : L"") +
|
||||
std::wstring(screenshotHotkey.alt ? L"Alt + " : L"") +
|
||||
std::wstring(L"VK ") + std::to_wstring(screenshotHotkey.key);
|
||||
|
||||
TraceLoggingWriteWrapper(
|
||||
g_hProvider,
|
||||
"CropAndLock_Settings",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingWideString(hotKeyStrReparent.c_str(), "ReparentHotKey"),
|
||||
TraceLoggingWideString(hotKeyStrThumbnail.c_str(), "ThumbnailHotkey"));
|
||||
TraceLoggingWideString(hotKeyStrThumbnail.c_str(), "ThumbnailHotkey"),
|
||||
TraceLoggingWideString(hotKeyStrScreenshot.c_str(), "ScreenshotHotkey"));
|
||||
}
|
||||
|
||||
@@ -12,8 +12,10 @@ public:
|
||||
static void Enable(bool enabled) noexcept;
|
||||
static void ActivateReparent() noexcept;
|
||||
static void ActivateThumbnail() noexcept;
|
||||
static void ActivateScreenshot() noexcept;
|
||||
static void CreateReparentWindow() noexcept;
|
||||
static void CreateThumbnailWindow() noexcept;
|
||||
static void SettingsTelemetry(PowertoyModuleIface::Hotkey&, PowertoyModuleIface::Hotkey&) noexcept;
|
||||
static void CreateScreenshotWindow() noexcept;
|
||||
static void SettingsTelemetry(PowertoyModuleIface::Hotkey&, PowertoyModuleIface::Hotkey&, PowertoyModuleIface::Hotkey&) noexcept;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace
|
||||
const wchar_t JSON_KEY_CODE[] = L"code";
|
||||
const wchar_t JSON_KEY_REPARENT_HOTKEY[] = L"reparent-hotkey";
|
||||
const wchar_t JSON_KEY_THUMBNAIL_HOTKEY[] = L"thumbnail-hotkey";
|
||||
const wchar_t JSON_KEY_SCREENSHOT_HOTKEY[] = L"screenshot-hotkey";
|
||||
const wchar_t JSON_KEY_VALUE[] = L"value";
|
||||
}
|
||||
|
||||
@@ -124,6 +125,10 @@ public:
|
||||
SetEvent(m_thumbnail_event_handle);
|
||||
Trace::CropAndLock::ActivateThumbnail();
|
||||
}
|
||||
if (hotkeyId == 2) { // Same order as set by get_hotkeys
|
||||
SetEvent(m_screenshot_event_handle);
|
||||
Trace::CropAndLock::ActivateScreenshot();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -133,12 +138,13 @@ public:
|
||||
|
||||
virtual size_t get_hotkeys(Hotkey* hotkeys, size_t buffer_size) override
|
||||
{
|
||||
if (hotkeys && buffer_size >= 2)
|
||||
if (hotkeys && buffer_size >= 3)
|
||||
{
|
||||
hotkeys[0] = m_reparent_hotkey;
|
||||
hotkeys[1] = m_thumbnail_hotkey;
|
||||
hotkeys[2] = m_screenshot_hotkey;
|
||||
}
|
||||
return 2;
|
||||
return 3;
|
||||
}
|
||||
|
||||
// Enable the powertoy
|
||||
@@ -171,7 +177,7 @@ public:
|
||||
virtual void send_settings_telemetry() override
|
||||
{
|
||||
Logger::info("Send settings telemetry");
|
||||
Trace::CropAndLock::SettingsTelemetry(m_reparent_hotkey, m_thumbnail_hotkey);
|
||||
Trace::CropAndLock::SettingsTelemetry(m_reparent_hotkey, m_thumbnail_hotkey, m_screenshot_hotkey);
|
||||
}
|
||||
|
||||
CropAndLockModuleInterface()
|
||||
@@ -182,6 +188,7 @@ public:
|
||||
|
||||
m_reparent_event_handle = CreateDefaultEvent(CommonSharedConstants::CROP_AND_LOCK_REPARENT_EVENT);
|
||||
m_thumbnail_event_handle = CreateDefaultEvent(CommonSharedConstants::CROP_AND_LOCK_THUMBNAIL_EVENT);
|
||||
m_screenshot_event_handle = CreateDefaultEvent(CommonSharedConstants::CROP_AND_LOCK_SCREENSHOT_EVENT);
|
||||
m_exit_event_handle = CreateDefaultEvent(CommonSharedConstants::CROP_AND_LOCK_EXIT_EVENT);
|
||||
|
||||
init_settings();
|
||||
@@ -202,6 +209,7 @@ private:
|
||||
|
||||
ResetEvent(m_reparent_event_handle);
|
||||
ResetEvent(m_thumbnail_event_handle);
|
||||
ResetEvent(m_screenshot_event_handle);
|
||||
ResetEvent(m_exit_event_handle);
|
||||
|
||||
SHELLEXECUTEINFOW sei{ sizeof(sei) };
|
||||
@@ -234,6 +242,7 @@ private:
|
||||
|
||||
ResetEvent(m_reparent_event_handle);
|
||||
ResetEvent(m_thumbnail_event_handle);
|
||||
ResetEvent(m_screenshot_event_handle);
|
||||
|
||||
// Log telemetry
|
||||
if (traceEvent)
|
||||
@@ -283,6 +292,21 @@ private:
|
||||
{
|
||||
Logger::error("Failed to initialize CropAndLock thumbnail shortcut from settings. Value will keep unchanged.");
|
||||
}
|
||||
try
|
||||
{
|
||||
Hotkey _temp_screenshot;
|
||||
auto jsonHotkeyObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_SCREENSHOT_HOTKEY).GetNamedObject(JSON_KEY_VALUE);
|
||||
_temp_screenshot.win = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_WIN);
|
||||
_temp_screenshot.alt = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_ALT);
|
||||
_temp_screenshot.shift = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_SHIFT);
|
||||
_temp_screenshot.ctrl = jsonHotkeyObject.GetNamedBoolean(JSON_KEY_CTRL);
|
||||
_temp_screenshot.key = static_cast<unsigned char>(jsonHotkeyObject.GetNamedNumber(JSON_KEY_CODE));
|
||||
m_screenshot_hotkey = _temp_screenshot;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Logger::error("Failed to initialize CropAndLock screenshot shortcut from settings. Value will keep unchanged.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -321,9 +345,11 @@ private:
|
||||
// TODO: actual default hotkey setting in line with other PowerToys.
|
||||
Hotkey m_reparent_hotkey = { .win = true, .ctrl = true, .shift = true, .alt = false, .key = 'R' };
|
||||
Hotkey m_thumbnail_hotkey = { .win = true, .ctrl = true, .shift = true, .alt = false, .key = 'T' };
|
||||
Hotkey m_screenshot_hotkey = { .win = true, .ctrl = true, .shift = true, .alt = false, .key = 'S' };
|
||||
|
||||
HANDLE m_reparent_event_handle;
|
||||
HANDLE m_thumbnail_event_handle;
|
||||
HANDLE m_screenshot_event_handle;
|
||||
HANDLE m_exit_event_handle;
|
||||
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToys.Interop;
|
||||
|
||||
@@ -21,15 +22,20 @@ internal sealed partial class CropAndLockReparentCommand : InvokableCommand
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
try
|
||||
Task.Run(async () =>
|
||||
{
|
||||
using var evt = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.CropAndLockReparentEvent());
|
||||
evt.Set();
|
||||
return CommandResult.Dismiss();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return CommandResult.ShowToast($"Failed to start Crop and Lock (Reparent): {ex.Message}");
|
||||
}
|
||||
await Task.Delay(500);
|
||||
try
|
||||
{
|
||||
using var evt = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.CropAndLockReparentEvent());
|
||||
evt.Set();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors after dismissing
|
||||
}
|
||||
});
|
||||
|
||||
return CommandResult.Dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToys.Interop;
|
||||
|
||||
namespace PowerToysExtension.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Triggers Crop and Lock screenshot mode via the shared event.
|
||||
/// </summary>
|
||||
internal sealed partial class CropAndLockScreenshotCommand : InvokableCommand
|
||||
{
|
||||
public CropAndLockScreenshotCommand()
|
||||
{
|
||||
Name = "Crop and Lock (Screenshot)";
|
||||
}
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(500);
|
||||
try
|
||||
{
|
||||
using var evt = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.CropAndLockScreenshotEvent());
|
||||
evt.Set();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors after dismissing
|
||||
}
|
||||
});
|
||||
|
||||
return CommandResult.Dismiss();
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using PowerToys.Interop;
|
||||
|
||||
@@ -21,15 +22,20 @@ internal sealed partial class CropAndLockThumbnailCommand : InvokableCommand
|
||||
|
||||
public override CommandResult Invoke()
|
||||
{
|
||||
try
|
||||
Task.Run(async () =>
|
||||
{
|
||||
using var evt = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.CropAndLockThumbnailEvent());
|
||||
evt.Set();
|
||||
return CommandResult.Dismiss();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return CommandResult.ShowToast($"Failed to start Crop and Lock (Thumbnail): {ex.Message}");
|
||||
}
|
||||
await Task.Delay(500);
|
||||
try
|
||||
{
|
||||
using var evt = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.CropAndLockThumbnailEvent());
|
||||
evt.Set();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors after dismissing
|
||||
}
|
||||
});
|
||||
|
||||
return CommandResult.Dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,13 @@ internal sealed class CropAndLockModuleCommandProvider : ModuleCommandProvider
|
||||
Subtitle = Resources.CropAndLock_Thumbnail_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
|
||||
yield return new ListItem(new CropAndLockScreenshotCommand())
|
||||
{
|
||||
Title = Resources.CropAndLock_Screenshot_Title,
|
||||
Subtitle = Resources.CropAndLock_Screenshot_Subtitle,
|
||||
Icon = icon,
|
||||
};
|
||||
}
|
||||
|
||||
yield return new ListItem(new OpenInSettingsCommand(module, title))
|
||||
|
||||
@@ -375,6 +375,24 @@ namespace PowerToysExtension.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Crop and Lock (Screenshot).
|
||||
/// </summary>
|
||||
internal static string CropAndLock_Screenshot_Title {
|
||||
get {
|
||||
return ResourceManager.GetString("CropAndLock_Screenshot_Title", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Create a cropped screenshot window.
|
||||
/// </summary>
|
||||
internal static string CropAndLock_Screenshot_Subtitle {
|
||||
get {
|
||||
return ResourceManager.GetString("CropAndLock_Screenshot_Subtitle", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Launch Environment Variables editor.
|
||||
/// </summary>
|
||||
|
||||
@@ -260,6 +260,12 @@
|
||||
<data name="CropAndLock_Thumbnail_Subtitle" xml:space="preserve">
|
||||
<value>Create a cropped thumbnail window</value>
|
||||
</data>
|
||||
<data name="CropAndLock_Screenshot_Title" xml:space="preserve">
|
||||
<value>Crop and Lock (Screenshot)</value>
|
||||
</data>
|
||||
<data name="CropAndLock_Screenshot_Subtitle" xml:space="preserve">
|
||||
<value>Create a cropped screenshot window</value>
|
||||
</data>
|
||||
<data name="CropAndLock_Settings_Subtitle" xml:space="preserve">
|
||||
<value>Open Crop and Lock settings</value>
|
||||
</data>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "general_settings.h"
|
||||
#include "auto_start_helper.h"
|
||||
#include "tray_icon.h"
|
||||
#include "quick_access_host.h"
|
||||
#include "Generated files/resource.h"
|
||||
#include "hotkey_conflict_detector.h"
|
||||
|
||||
@@ -72,6 +73,8 @@ static bool download_updates_automatically = true;
|
||||
static bool show_whats_new_after_updates = true;
|
||||
static bool enable_experimentation = true;
|
||||
static bool enable_warnings_elevated_apps = true;
|
||||
static bool enable_quick_access = true;
|
||||
static PowerToysSettings::HotkeyObject quick_access_shortcut;
|
||||
static DashboardSortOrder dashboard_sort_order = DashboardSortOrder::Alphabetical;
|
||||
static json::JsonObject ignored_conflict_properties = create_default_ignored_conflict_properties();
|
||||
|
||||
@@ -105,6 +108,8 @@ json::JsonObject GeneralSettings::to_json()
|
||||
result.SetNamedValue(L"dashboard_sort_order", json::value(static_cast<int>(dashboardSortOrder)));
|
||||
result.SetNamedValue(L"is_admin", json::value(isAdmin));
|
||||
result.SetNamedValue(L"enable_warnings_elevated_apps", json::value(enableWarningsElevatedApps));
|
||||
result.SetNamedValue(L"enable_quick_access", json::value(enableQuickAccess));
|
||||
result.SetNamedValue(L"quick_access_shortcut", quickAccessShortcut.get_json());
|
||||
result.SetNamedValue(L"theme", json::value(theme));
|
||||
result.SetNamedValue(L"system_theme", json::value(systemTheme));
|
||||
result.SetNamedValue(L"powertoys_version", json::value(powerToysVersion));
|
||||
@@ -127,6 +132,11 @@ json::JsonObject load_general_settings()
|
||||
show_whats_new_after_updates = loaded.GetNamedBoolean(L"show_whats_new_after_updates", true);
|
||||
enable_experimentation = loaded.GetNamedBoolean(L"enable_experimentation", true);
|
||||
enable_warnings_elevated_apps = loaded.GetNamedBoolean(L"enable_warnings_elevated_apps", true);
|
||||
enable_quick_access = loaded.GetNamedBoolean(L"enable_quick_access", true);
|
||||
if (json::has(loaded, L"quick_access_shortcut", json::JsonValueType::Object))
|
||||
{
|
||||
quick_access_shortcut = PowerToysSettings::HotkeyObject::from_json(loaded.GetNamedObject(L"quick_access_shortcut"));
|
||||
}
|
||||
dashboard_sort_order = parse_dashboard_sort_order(loaded, dashboard_sort_order);
|
||||
|
||||
if (json::has(loaded, L"ignored_conflict_properties", json::JsonValueType::Object))
|
||||
@@ -153,6 +163,8 @@ GeneralSettings get_general_settings()
|
||||
.isRunElevated = run_as_elevated,
|
||||
.isAdmin = is_user_admin,
|
||||
.enableWarningsElevatedApps = enable_warnings_elevated_apps,
|
||||
.enableQuickAccess = enable_quick_access,
|
||||
.quickAccessShortcut = quick_access_shortcut,
|
||||
.showNewUpdatesToastNotification = show_new_updates_toast_notification,
|
||||
.downloadUpdatesAutomatically = download_updates_automatically && is_user_admin,
|
||||
.showWhatsNewAfterUpdates = show_whats_new_after_updates,
|
||||
@@ -178,11 +190,47 @@ GeneralSettings get_general_settings()
|
||||
|
||||
void apply_general_settings(const json::JsonObject& general_configs, bool save)
|
||||
{
|
||||
std::wstring old_settings_json_string;
|
||||
if (save)
|
||||
{
|
||||
old_settings_json_string = get_general_settings().to_json().Stringify().c_str();
|
||||
}
|
||||
|
||||
Logger::info(L"apply_general_settings: {}", std::wstring{ general_configs.ToString() });
|
||||
run_as_elevated = general_configs.GetNamedBoolean(L"run_elevated", false);
|
||||
|
||||
enable_warnings_elevated_apps = general_configs.GetNamedBoolean(L"enable_warnings_elevated_apps", true);
|
||||
|
||||
bool new_enable_quick_access = general_configs.GetNamedBoolean(L"enable_quick_access", true);
|
||||
Logger::info(L"apply_general_settings: enable_quick_access={}, new_enable_quick_access={}", enable_quick_access, new_enable_quick_access);
|
||||
|
||||
PowerToysSettings::HotkeyObject new_quick_access_shortcut;
|
||||
if (json::has(general_configs, L"quick_access_shortcut", json::JsonValueType::Object))
|
||||
{
|
||||
new_quick_access_shortcut = PowerToysSettings::HotkeyObject::from_json(general_configs.GetNamedObject(L"quick_access_shortcut"));
|
||||
}
|
||||
|
||||
auto hotkey_equals = [](const PowerToysSettings::HotkeyObject& a, const PowerToysSettings::HotkeyObject& b) {
|
||||
return a.get_code() == b.get_code() &&
|
||||
a.get_modifiers() == b.get_modifiers();
|
||||
};
|
||||
|
||||
if (enable_quick_access != new_enable_quick_access || !hotkey_equals(quick_access_shortcut, new_quick_access_shortcut))
|
||||
{
|
||||
enable_quick_access = new_enable_quick_access;
|
||||
quick_access_shortcut = new_quick_access_shortcut;
|
||||
|
||||
if (enable_quick_access)
|
||||
{
|
||||
QuickAccessHost::start();
|
||||
}
|
||||
else
|
||||
{
|
||||
QuickAccessHost::stop();
|
||||
}
|
||||
update_quick_access_hotkey(enable_quick_access, quick_access_shortcut);
|
||||
}
|
||||
|
||||
show_new_updates_toast_notification = general_configs.GetNamedBoolean(L"show_new_updates_toast_notification", true);
|
||||
|
||||
download_updates_automatically = general_configs.GetNamedBoolean(L"download_updates_automatically", true);
|
||||
@@ -321,8 +369,12 @@ void apply_general_settings(const json::JsonObject& general_configs, bool save)
|
||||
if (save)
|
||||
{
|
||||
GeneralSettings save_settings = get_general_settings();
|
||||
PTSettingsHelper::save_general_settings(save_settings.to_json());
|
||||
Trace::SettingsChanged(save_settings);
|
||||
std::wstring new_settings_json_string = save_settings.to_json().Stringify().c_str();
|
||||
if (old_settings_json_string != new_settings_json_string)
|
||||
{
|
||||
PTSettingsHelper::save_general_settings(save_settings.to_json());
|
||||
Trace::SettingsChanged(save_settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -412,3 +464,5 @@ void start_enabled_powertoys()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/utils/json.h>
|
||||
#include <common/SettingsAPI/settings_objects.h>
|
||||
|
||||
enum class DashboardSortOrder
|
||||
{
|
||||
@@ -18,6 +19,8 @@ struct GeneralSettings
|
||||
bool isRunElevated;
|
||||
bool isAdmin;
|
||||
bool enableWarningsElevatedApps;
|
||||
bool enableQuickAccess;
|
||||
PowerToysSettings::HotkeyObject quickAccessShortcut;
|
||||
bool showNewUpdatesToastNotification;
|
||||
bool downloadUpdatesAutomatically;
|
||||
bool showWhatsNewAfterUpdates;
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include <shellapi.h>
|
||||
#include "centralized_kb_hook.h"
|
||||
#include "centralized_hotkeys.h"
|
||||
#include "quick_access_host.h"
|
||||
#include "ai_detection.h"
|
||||
#include <common/utils/package.h>
|
||||
|
||||
@@ -189,6 +190,11 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow
|
||||
#endif
|
||||
Trace::RegisterProvider();
|
||||
start_tray_icon(isProcessElevated);
|
||||
if (get_general_settings().enableQuickAccess)
|
||||
{
|
||||
QuickAccessHost::start();
|
||||
}
|
||||
update_quick_access_hotkey(get_general_settings().enableQuickAccess, get_general_settings().quickAccessShortcut);
|
||||
set_tray_icon_visible(get_general_settings().showSystemTrayIcon);
|
||||
CentralizedKeyboardHook::Start();
|
||||
|
||||
@@ -316,7 +322,7 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow
|
||||
{
|
||||
window = winrt::to_hstring(settingsWindow);
|
||||
}
|
||||
open_settings_window(window, false);
|
||||
open_settings_window(window);
|
||||
}
|
||||
|
||||
if (openOobe)
|
||||
@@ -339,6 +345,7 @@ int runner(bool isProcessElevated, bool openSettings, std::string settingsWindow
|
||||
result = -1;
|
||||
}
|
||||
Trace::UnregisterProvider();
|
||||
QuickAccessHost::stop();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
269
src/runner/quick_access_host.cpp
Normal file
269
src/runner/quick_access_host.cpp
Normal file
@@ -0,0 +1,269 @@
|
||||
#include "pch.h"
|
||||
#include "quick_access_host.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <rpc.h>
|
||||
#include <new>
|
||||
#include <memory>
|
||||
|
||||
#include <common/logger/logger.h>
|
||||
#include <common/utils/process_path.h>
|
||||
#include <common/interop/two_way_pipe_message_ipc.h>
|
||||
#include <wil/resource.h>
|
||||
|
||||
extern void receive_json_send_to_main_thread(const std::wstring& msg);
|
||||
|
||||
namespace
|
||||
{
|
||||
wil::unique_handle quick_access_process;
|
||||
wil::unique_handle show_event;
|
||||
wil::unique_handle exit_event;
|
||||
std::wstring show_event_name;
|
||||
std::wstring exit_event_name;
|
||||
std::wstring runner_pipe_name;
|
||||
std::wstring app_pipe_name;
|
||||
std::unique_ptr<TwoWayPipeMessageIPC> quick_access_ipc;
|
||||
std::mutex quick_access_mutex;
|
||||
|
||||
bool is_process_active_locked()
|
||||
{
|
||||
if (!quick_access_process)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD exit_code = 0;
|
||||
if (!GetExitCodeProcess(quick_access_process.get(), &exit_code))
|
||||
{
|
||||
Logger::warn(L"QuickAccessHost: failed to read Quick Access process exit code. error={}.", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return exit_code == STILL_ACTIVE;
|
||||
}
|
||||
|
||||
void reset_state_locked()
|
||||
{
|
||||
if (quick_access_ipc)
|
||||
{
|
||||
quick_access_ipc->end();
|
||||
quick_access_ipc.reset();
|
||||
}
|
||||
|
||||
quick_access_process.reset();
|
||||
show_event.reset();
|
||||
exit_event.reset();
|
||||
show_event_name.clear();
|
||||
exit_event_name.clear();
|
||||
runner_pipe_name.clear();
|
||||
app_pipe_name.clear();
|
||||
}
|
||||
|
||||
std::wstring build_event_name(const wchar_t* suffix)
|
||||
{
|
||||
std::wstring name = L"Local\\PowerToysQuickAccess_";
|
||||
name += std::to_wstring(GetCurrentProcessId());
|
||||
if (suffix)
|
||||
{
|
||||
name += suffix;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
std::wstring build_command_line(const std::wstring& exe_path)
|
||||
{
|
||||
std::wstring command_line = L"\"";
|
||||
command_line += exe_path;
|
||||
command_line += L"\" --show-event=\"";
|
||||
command_line += show_event_name;
|
||||
command_line += L"\" --exit-event=\"";
|
||||
command_line += exit_event_name;
|
||||
command_line += L"\"";
|
||||
if (!runner_pipe_name.empty())
|
||||
{
|
||||
command_line.append(L" --runner-pipe=\"");
|
||||
command_line += runner_pipe_name;
|
||||
command_line += L"\"";
|
||||
}
|
||||
if (!app_pipe_name.empty())
|
||||
{
|
||||
command_line.append(L" --app-pipe=\"");
|
||||
command_line += app_pipe_name;
|
||||
command_line += L"\"";
|
||||
}
|
||||
return command_line;
|
||||
}
|
||||
}
|
||||
|
||||
namespace QuickAccessHost
|
||||
{
|
||||
bool is_running()
|
||||
{
|
||||
std::scoped_lock lock(quick_access_mutex);
|
||||
return is_process_active_locked();
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
Logger::info(L"QuickAccessHost::start() called");
|
||||
std::scoped_lock lock(quick_access_mutex);
|
||||
if (is_process_active_locked())
|
||||
{
|
||||
Logger::info(L"QuickAccessHost::start: process already active");
|
||||
return;
|
||||
}
|
||||
|
||||
reset_state_locked();
|
||||
|
||||
show_event_name = build_event_name(L"_Show");
|
||||
exit_event_name = build_event_name(L"_Exit");
|
||||
|
||||
show_event.reset(CreateEventW(nullptr, FALSE, FALSE, show_event_name.c_str()));
|
||||
if (!show_event)
|
||||
{
|
||||
Logger::error(L"QuickAccessHost: failed to create show event. error={}.", GetLastError());
|
||||
reset_state_locked();
|
||||
return;
|
||||
}
|
||||
|
||||
exit_event.reset(CreateEventW(nullptr, FALSE, FALSE, exit_event_name.c_str()));
|
||||
if (!exit_event)
|
||||
{
|
||||
Logger::error(L"QuickAccessHost: failed to create exit event. error={}.", GetLastError());
|
||||
reset_state_locked();
|
||||
return;
|
||||
}
|
||||
|
||||
runner_pipe_name = L"\\\\.\\pipe\\powertoys_quick_access_runner_";
|
||||
app_pipe_name = L"\\\\.\\pipe\\powertoys_quick_access_ui_";
|
||||
UUID temp_uuid;
|
||||
wchar_t* uuid_chars = nullptr;
|
||||
if (UuidCreate(&temp_uuid) == RPC_S_UUID_NO_ADDRESS)
|
||||
{
|
||||
Logger::warn(L"QuickAccessHost: failed to create UUID for pipe names. error={}.", GetLastError());
|
||||
}
|
||||
else if (UuidToString(&temp_uuid, reinterpret_cast<RPC_WSTR*>(&uuid_chars)) != RPC_S_OK)
|
||||
{
|
||||
Logger::warn(L"QuickAccessHost: failed to convert UUID to string. error={}.", GetLastError());
|
||||
}
|
||||
|
||||
if (uuid_chars != nullptr)
|
||||
{
|
||||
runner_pipe_name += std::wstring(uuid_chars);
|
||||
app_pipe_name += std::wstring(uuid_chars);
|
||||
RpcStringFree(reinterpret_cast<RPC_WSTR*>(&uuid_chars));
|
||||
uuid_chars = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::wstring fallback_suffix = std::to_wstring(GetTickCount64());
|
||||
runner_pipe_name += fallback_suffix;
|
||||
app_pipe_name += fallback_suffix;
|
||||
}
|
||||
|
||||
HANDLE token_handle = nullptr;
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token_handle))
|
||||
{
|
||||
Logger::error(L"QuickAccessHost: failed to open process token. error={}.", GetLastError());
|
||||
reset_state_locked();
|
||||
return;
|
||||
}
|
||||
|
||||
wil::unique_handle token(token_handle);
|
||||
quick_access_ipc.reset(new (std::nothrow) TwoWayPipeMessageIPC(runner_pipe_name, app_pipe_name, receive_json_send_to_main_thread));
|
||||
if (!quick_access_ipc)
|
||||
{
|
||||
Logger::error(L"QuickAccessHost: failed to allocate IPC instance.");
|
||||
reset_state_locked();
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
quick_access_ipc->start(token.get());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Logger::error(L"QuickAccessHost: failed to start IPC server for Quick Access.");
|
||||
reset_state_locked();
|
||||
return;
|
||||
}
|
||||
|
||||
const std::wstring exe_path = get_module_folderpath() + L"\\WinUI3Apps\\PowerToys.QuickAccess.exe";
|
||||
if (GetFileAttributesW(exe_path.c_str()) == INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
Logger::warn(L"QuickAccessHost: missing Quick Access executable at {}", exe_path);
|
||||
reset_state_locked();
|
||||
return;
|
||||
}
|
||||
|
||||
const std::wstring command_line = build_command_line(exe_path);
|
||||
std::vector<wchar_t> command_line_buffer(command_line.begin(), command_line.end());
|
||||
command_line_buffer.push_back(L'\0');
|
||||
STARTUPINFOW startup_info{};
|
||||
startup_info.cb = sizeof(startup_info);
|
||||
PROCESS_INFORMATION process_info{};
|
||||
|
||||
BOOL created = CreateProcessW(exe_path.c_str(), command_line_buffer.data(), nullptr, nullptr, FALSE, 0, nullptr, nullptr, &startup_info, &process_info);
|
||||
if (!created)
|
||||
{
|
||||
Logger::error(L"QuickAccessHost: failed to launch Quick Access host. error={}.", GetLastError());
|
||||
reset_state_locked();
|
||||
return;
|
||||
}
|
||||
|
||||
quick_access_process.reset(process_info.hProcess);
|
||||
CloseHandle(process_info.hThread);
|
||||
}
|
||||
|
||||
void show()
|
||||
{
|
||||
start();
|
||||
std::scoped_lock lock(quick_access_mutex);
|
||||
|
||||
if (show_event)
|
||||
{
|
||||
if (!SetEvent(show_event.get()))
|
||||
{
|
||||
Logger::warn(L"QuickAccessHost: failed to signal show event. error={}.", GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
Logger::info(L"QuickAccessHost::stop() called");
|
||||
std::unique_lock lock(quick_access_mutex);
|
||||
if (exit_event)
|
||||
{
|
||||
SetEvent(exit_event.get());
|
||||
}
|
||||
|
||||
if (quick_access_process)
|
||||
{
|
||||
const DWORD wait_result = WaitForSingleObject(quick_access_process.get(), 2000);
|
||||
Logger::info(L"QuickAccessHost::stop: WaitForSingleObject result={}", wait_result);
|
||||
if (wait_result == WAIT_TIMEOUT)
|
||||
{
|
||||
Logger::warn(L"QuickAccessHost: Quick Access process did not exit in time, terminating.");
|
||||
if (!TerminateProcess(quick_access_process.get(), 0))
|
||||
{
|
||||
Logger::error(L"QuickAccessHost: failed to terminate Quick Access process. error={}.", GetLastError());
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::info(L"QuickAccessHost: TerminateProcess succeeded.");
|
||||
WaitForSingleObject(quick_access_process.get(), 5000);
|
||||
}
|
||||
}
|
||||
else if (wait_result == WAIT_FAILED)
|
||||
{
|
||||
Logger::error(L"QuickAccessHost: failed while waiting for Quick Access process. error={}.", GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
reset_state_locked();
|
||||
}
|
||||
}
|
||||
12
src/runner/quick_access_host.h
Normal file
12
src/runner/quick_access_host.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <Windows.h>
|
||||
#include <optional>
|
||||
|
||||
namespace QuickAccessHost
|
||||
{
|
||||
void start();
|
||||
void show();
|
||||
void stop();
|
||||
bool is_running();
|
||||
}
|
||||
@@ -70,6 +70,7 @@
|
||||
</ClCompile>
|
||||
<ClCompile Include="powertoy_module.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="quick_access_host.cpp" />
|
||||
<ClCompile Include="restart_elevated.cpp" />
|
||||
<ClCompile Include="centralized_kb_hook.cpp" />
|
||||
<ClCompile Include="settings_telemetry.cpp" />
|
||||
@@ -85,6 +86,7 @@
|
||||
<ClInclude Include="auto_start_helper.h" />
|
||||
<ClInclude Include="bug_report.h" />
|
||||
<ClInclude Include="centralized_hotkeys.h" />
|
||||
<ClInclude Include="quick_access_host.h" />
|
||||
<ClInclude Include="general_settings.h" />
|
||||
<ClInclude Include="hotkey_conflict_detector.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
|
||||
@@ -48,6 +48,9 @@
|
||||
<ClCompile Include="hotkey_conflict_detector.cpp">
|
||||
<Filter>Utils</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="quick_access_host.cpp">
|
||||
<Filter>Utils</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
@@ -102,6 +105,9 @@
|
||||
<ClInclude Include="hotkey_conflict_detector.h">
|
||||
<Filter>Utils</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="quick_access_host.h">
|
||||
<Filter>Utils</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Utils">
|
||||
|
||||
@@ -198,6 +198,8 @@ void dispatch_received_json(const std::wstring& json_to_parse)
|
||||
return;
|
||||
}
|
||||
|
||||
Logger::info(L"dispatch_received_json: {}", json_to_parse);
|
||||
|
||||
for (const auto& base_element : j)
|
||||
{
|
||||
const auto name = base_element.Key();
|
||||
@@ -206,12 +208,12 @@ void dispatch_received_json(const std::wstring& json_to_parse)
|
||||
if (name == L"general")
|
||||
{
|
||||
apply_general_settings(value.GetObjectW());
|
||||
const std::wstring settings_string{ get_all_settings().Stringify().c_str() };
|
||||
{
|
||||
std::unique_lock lock{ ipc_mutex };
|
||||
if (current_settings_ipc)
|
||||
current_settings_ipc->send(settings_string);
|
||||
}
|
||||
// const std::wstring settings_string{ get_all_settings().Stringify().c_str() };
|
||||
// {
|
||||
// std::unique_lock lock{ ipc_mutex };
|
||||
// if (current_settings_ipc)
|
||||
// current_settings_ipc->send(settings_string);
|
||||
// }
|
||||
}
|
||||
else if (name == L"powertoys")
|
||||
{
|
||||
@@ -421,7 +423,7 @@ BOOL run_settings_non_elevated(LPCWSTR executable_path, LPWSTR executable_args,
|
||||
|
||||
DWORD g_settings_process_id = 0;
|
||||
|
||||
void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::optional<std::wstring> settings_window, bool show_flyout = false, const std::optional<POINT>& flyout_position = std::nullopt)
|
||||
void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::optional<std::wstring> settings_window)
|
||||
{
|
||||
g_isLaunchInProgress = true;
|
||||
|
||||
@@ -491,22 +493,16 @@ void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::op
|
||||
// Arg 9: should scoobe window be shown
|
||||
std::wstring settings_showScoobe = show_scoobe_window ? L"true" : L"false";
|
||||
|
||||
// Arg 10: should flyout be shown
|
||||
std::wstring settings_showFlyout = show_flyout ? L"true" : L"false";
|
||||
|
||||
// Arg 11: contains if there's a settings window argument. If true, will add one extra argument with the value to the call.
|
||||
// Arg 10: contains if there's a settings window argument. If true, will add one extra argument with the value to the call.
|
||||
std::wstring settings_containsSettingsWindow = settings_window.has_value() ? L"true" : L"false";
|
||||
|
||||
// Arg 12: contains if there's flyout coordinates. If true, will add two extra arguments to the call containing the x and y coordinates.
|
||||
std::wstring settings_containsFlyoutPosition = flyout_position.has_value() ? L"true" : L"false";
|
||||
|
||||
// Args 13, .... : Optional arguments depending on the options presented before. All by the same value.
|
||||
// Args 11, .... : Optional arguments depending on the options presented before. All by the same value.
|
||||
|
||||
// create general settings file to initialize the settings file with installation configurations like :
|
||||
// 1. Run on start up.
|
||||
PTSettingsHelper::save_general_settings(save_settings.to_json());
|
||||
|
||||
std::wstring executable_args = fmt::format(L"\"{}\" {} {} {} {} {} {} {} {} {} {} {}",
|
||||
std::wstring executable_args = fmt::format(L"\"{}\" {} {} {} {} {} {} {} {} {}",
|
||||
executable_path,
|
||||
powertoys_pipe_name,
|
||||
settings_pipe_name,
|
||||
@@ -516,9 +512,7 @@ void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::op
|
||||
settings_isUserAnAdmin,
|
||||
settings_showOobe,
|
||||
settings_showScoobe,
|
||||
settings_showFlyout,
|
||||
settings_containsSettingsWindow,
|
||||
settings_containsFlyoutPosition);
|
||||
settings_containsSettingsWindow);
|
||||
|
||||
if (settings_window.has_value())
|
||||
{
|
||||
@@ -526,14 +520,6 @@ void run_settings_window(bool show_oobe_window, bool show_scoobe_window, std::op
|
||||
executable_args.append(settings_window.value());
|
||||
}
|
||||
|
||||
if (flyout_position)
|
||||
{
|
||||
executable_args.append(L" ");
|
||||
executable_args.append(std::to_wstring(flyout_position.value().x));
|
||||
executable_args.append(L" ");
|
||||
executable_args.append(std::to_wstring(flyout_position.value().y));
|
||||
}
|
||||
|
||||
BOOL process_created = false;
|
||||
|
||||
// Commented out to fix #22659
|
||||
@@ -684,39 +670,22 @@ void bring_settings_to_front()
|
||||
EnumWindows(callback, 0);
|
||||
}
|
||||
|
||||
void open_settings_window(std::optional<std::wstring> settings_window, bool show_flyout = false, const std::optional<POINT>& flyout_position)
|
||||
void open_settings_window(std::optional<std::wstring> settings_window)
|
||||
{
|
||||
if (g_settings_process_id != 0)
|
||||
{
|
||||
if (show_flyout)
|
||||
// nl instead of showing the window, send message to it (flyout might need to be hidden, main setting window activated)
|
||||
// bring_settings_to_front();
|
||||
if (current_settings_ipc)
|
||||
{
|
||||
if (current_settings_ipc)
|
||||
if (settings_window.has_value())
|
||||
{
|
||||
if (!flyout_position.has_value())
|
||||
{
|
||||
current_settings_ipc->send(L"{\"ShowYourself\":\"flyout\"}");
|
||||
}
|
||||
else
|
||||
{
|
||||
current_settings_ipc->send(fmt::format(L"{{\"ShowYourself\":\"flyout\", \"x_position\":{}, \"y_position\":{} }}", std::to_wstring(flyout_position.value().x), std::to_wstring(flyout_position.value().y)));
|
||||
}
|
||||
std::wstring msg = L"{\"ShowYourself\":\"" + settings_window.value() + L"\"}";
|
||||
current_settings_ipc->send(msg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// nl instead of showing the window, send message to it (flyout might need to be hidden, main setting window activated)
|
||||
// bring_settings_to_front();
|
||||
if (current_settings_ipc)
|
||||
else
|
||||
{
|
||||
if (settings_window.has_value())
|
||||
{
|
||||
std::wstring msg = L"{\"ShowYourself\":\"" + settings_window.value() + L"\"}";
|
||||
current_settings_ipc->send(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
current_settings_ipc->send(L"{\"ShowYourself\":\"Dashboard\"}");
|
||||
}
|
||||
current_settings_ipc->send(L"{\"ShowYourself\":\"Dashboard\"}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -724,8 +693,8 @@ void open_settings_window(std::optional<std::wstring> settings_window, bool show
|
||||
{
|
||||
if (!g_isLaunchInProgress)
|
||||
{
|
||||
std::thread([settings_window, show_flyout, flyout_position]() {
|
||||
run_settings_window(false, false, settings_window, show_flyout, flyout_position);
|
||||
std::thread([settings_window]() {
|
||||
run_settings_window(false, false, settings_window);
|
||||
}).detach();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,9 +41,8 @@ enum class ESettingsWindowNames
|
||||
std::string ESettingsWindowNames_to_string(ESettingsWindowNames value);
|
||||
ESettingsWindowNames ESettingsWindowNames_from_string(std::string value);
|
||||
|
||||
void open_settings_window(std::optional<std::wstring> settings_window, bool show_flyout, const std::optional<POINT>& flyout_position);
|
||||
void open_settings_window(std::optional<std::wstring> settings_window);
|
||||
void close_settings_window();
|
||||
|
||||
void open_oobe_window();
|
||||
void open_scoobe_window();
|
||||
void open_flyout();
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "general_settings.h"
|
||||
#include "centralized_hotkeys.h"
|
||||
#include "centralized_kb_hook.h"
|
||||
#include "quick_access_host.h"
|
||||
#include "hotkey_conflict_detector.h"
|
||||
#include <Windows.h>
|
||||
|
||||
#include <common/utils/resources.h>
|
||||
@@ -69,9 +71,9 @@ void change_menu_item_text(const UINT item_id, wchar_t* new_text)
|
||||
SetMenuItemInfoW(h_menu, item_id, false, &menuitem);
|
||||
}
|
||||
|
||||
void open_quick_access_flyout_window(const POINT flyout_position)
|
||||
void open_quick_access_flyout_window()
|
||||
{
|
||||
open_settings_window(std::nullopt, true, flyout_position);
|
||||
QuickAccessHost::show();
|
||||
}
|
||||
|
||||
void handle_tray_command(HWND window, const WPARAM command_id, LPARAM lparam)
|
||||
@@ -81,7 +83,7 @@ void handle_tray_command(HWND window, const WPARAM command_id, LPARAM lparam)
|
||||
case ID_SETTINGS_MENU_COMMAND:
|
||||
{
|
||||
std::wstring settings_window{ winrt::to_hstring(ESettingsWindowNames_to_string(static_cast<ESettingsWindowNames>(lparam))) };
|
||||
open_settings_window(settings_window, false);
|
||||
open_settings_window(settings_window);
|
||||
}
|
||||
break;
|
||||
case ID_CLOSE_MENU_COMMAND:
|
||||
@@ -113,9 +115,7 @@ void handle_tray_command(HWND window, const WPARAM command_id, LPARAM lparam)
|
||||
}
|
||||
case ID_QUICK_ACCESS_MENU_COMMAND:
|
||||
{
|
||||
POINT mouse_pointer;
|
||||
GetCursorPos(&mouse_pointer);
|
||||
open_quick_access_flyout_window(mouse_pointer);
|
||||
open_quick_access_flyout_window();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -126,7 +126,14 @@ void click_timer_elapsed()
|
||||
double_click_timer_running = false;
|
||||
if (!double_clicked)
|
||||
{
|
||||
open_quick_access_flyout_window(tray_icon_click_point);
|
||||
if (get_general_settings().enableQuickAccess)
|
||||
{
|
||||
open_quick_access_flyout_window();
|
||||
}
|
||||
else
|
||||
{
|
||||
open_settings_window(std::nullopt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,9 +225,6 @@ LRESULT __stdcall tray_icon_window_proc(HWND window, UINT message, WPARAM wparam
|
||||
// ignore event if this is the second click of a double click
|
||||
if (!double_click_timer_running)
|
||||
{
|
||||
// save the cursor position for sending where to show the popup.
|
||||
GetCursorPos(&tray_icon_click_point);
|
||||
|
||||
// start timer for detecting single or double click
|
||||
double_click_timer_running = true;
|
||||
double_clicked = false;
|
||||
@@ -236,7 +240,7 @@ LRESULT __stdcall tray_icon_window_proc(HWND window, UINT message, WPARAM wparam
|
||||
case WM_LBUTTONDBLCLK:
|
||||
{
|
||||
double_clicked = true;
|
||||
open_settings_window(std::nullopt, false);
|
||||
open_settings_window(std::nullopt);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@@ -349,4 +353,37 @@ void stop_tray_icon()
|
||||
BugReportManager::instance().clear_callbacks();
|
||||
SendMessage(tray_icon_hwnd, WM_CLOSE, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
void update_quick_access_hotkey(bool enabled, PowerToysSettings::HotkeyObject hotkey)
|
||||
{
|
||||
static PowerToysSettings::HotkeyObject current_hotkey;
|
||||
static bool is_registered = false;
|
||||
auto& hkmng = HotkeyConflictDetector::HotkeyConflictManager::GetInstance();
|
||||
|
||||
if (is_registered)
|
||||
{
|
||||
CentralizedKeyboardHook::ClearModuleHotkeys(L"QuickAccess");
|
||||
hkmng.RemoveHotkeyByModule(L"GeneralSettings");
|
||||
is_registered = false;
|
||||
}
|
||||
|
||||
if (enabled && hotkey.get_code() != 0)
|
||||
{
|
||||
HotkeyConflictDetector::Hotkey hk = {
|
||||
hotkey.win_pressed(),
|
||||
hotkey.ctrl_pressed(),
|
||||
hotkey.shift_pressed(),
|
||||
hotkey.alt_pressed(),
|
||||
static_cast<unsigned char>(hotkey.get_code())
|
||||
};
|
||||
|
||||
hkmng.AddHotkey(hk, L"GeneralSettings", 0, true);
|
||||
CentralizedKeyboardHook::SetHotkeyAction(L"QuickAccess", hk, []() {
|
||||
open_quick_access_flyout_window();
|
||||
return true;
|
||||
});
|
||||
|
||||
current_hotkey = hotkey;
|
||||
is_registered = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <common/SettingsAPI/settings_objects.h>
|
||||
|
||||
// Start the Tray Icon
|
||||
void start_tray_icon(bool isProcessElevated);
|
||||
@@ -9,7 +10,9 @@ void set_tray_icon_visible(bool shouldIconBeVisible);
|
||||
// Stop the Tray Icon
|
||||
void stop_tray_icon();
|
||||
// Open the Settings Window
|
||||
void open_settings_window(std::optional<std::wstring> settings_window, bool show_flyout, const std::optional<POINT>& flyout_position = std::nullopt);
|
||||
void open_settings_window(std::optional<std::wstring> settings_window);
|
||||
// Update Quick Access Hotkey
|
||||
void update_quick_access_hotkey(bool enabled, PowerToysSettings::HotkeyObject hotkey);
|
||||
// Callback type to be called by the tray icon loop
|
||||
typedef void (*main_loop_callback_function)(PVOID);
|
||||
// Calls a callback in _callback
|
||||
|
||||
49
src/settings-ui/QuickAccess.UI/Helpers/ModuleGpoHelper.cs
Normal file
49
src/settings-ui/QuickAccess.UI/Helpers/ModuleGpoHelper.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
// 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 global::PowerToys.GPOWrapper;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess.Helpers;
|
||||
|
||||
internal static class ModuleGpoHelper
|
||||
{
|
||||
public static GpoRuleConfigured GetModuleGpoConfiguration(ModuleType moduleType)
|
||||
{
|
||||
return moduleType switch
|
||||
{
|
||||
ModuleType.AdvancedPaste => GPOWrapper.GetConfiguredAdvancedPasteEnabledValue(),
|
||||
ModuleType.AlwaysOnTop => GPOWrapper.GetConfiguredAlwaysOnTopEnabledValue(),
|
||||
ModuleType.Awake => GPOWrapper.GetConfiguredAwakeEnabledValue(),
|
||||
ModuleType.CmdPal => GPOWrapper.GetConfiguredCmdPalEnabledValue(),
|
||||
ModuleType.ColorPicker => GPOWrapper.GetConfiguredColorPickerEnabledValue(),
|
||||
ModuleType.CropAndLock => GPOWrapper.GetConfiguredCropAndLockEnabledValue(),
|
||||
ModuleType.CursorWrap => GPOWrapper.GetConfiguredCursorWrapEnabledValue(),
|
||||
ModuleType.EnvironmentVariables => GPOWrapper.GetConfiguredEnvironmentVariablesEnabledValue(),
|
||||
ModuleType.FancyZones => GPOWrapper.GetConfiguredFancyZonesEnabledValue(),
|
||||
ModuleType.FileLocksmith => GPOWrapper.GetConfiguredFileLocksmithEnabledValue(),
|
||||
ModuleType.FindMyMouse => GPOWrapper.GetConfiguredFindMyMouseEnabledValue(),
|
||||
ModuleType.Hosts => GPOWrapper.GetConfiguredHostsFileEditorEnabledValue(),
|
||||
ModuleType.ImageResizer => GPOWrapper.GetConfiguredImageResizerEnabledValue(),
|
||||
ModuleType.KeyboardManager => GPOWrapper.GetConfiguredKeyboardManagerEnabledValue(),
|
||||
ModuleType.MouseHighlighter => GPOWrapper.GetConfiguredMouseHighlighterEnabledValue(),
|
||||
ModuleType.MouseJump => GPOWrapper.GetConfiguredMouseJumpEnabledValue(),
|
||||
ModuleType.MousePointerCrosshairs => GPOWrapper.GetConfiguredMousePointerCrosshairsEnabledValue(),
|
||||
ModuleType.MouseWithoutBorders => GPOWrapper.GetConfiguredMouseWithoutBordersEnabledValue(),
|
||||
ModuleType.NewPlus => GPOWrapper.GetConfiguredNewPlusEnabledValue(),
|
||||
ModuleType.Peek => GPOWrapper.GetConfiguredPeekEnabledValue(),
|
||||
ModuleType.PowerRename => GPOWrapper.GetConfiguredPowerRenameEnabledValue(),
|
||||
ModuleType.PowerLauncher => GPOWrapper.GetConfiguredPowerLauncherEnabledValue(),
|
||||
ModuleType.PowerAccent => GPOWrapper.GetConfiguredQuickAccentEnabledValue(),
|
||||
ModuleType.Workspaces => GPOWrapper.GetConfiguredWorkspacesEnabledValue(),
|
||||
ModuleType.RegistryPreview => GPOWrapper.GetConfiguredRegistryPreviewEnabledValue(),
|
||||
ModuleType.MeasureTool => GPOWrapper.GetConfiguredScreenRulerEnabledValue(),
|
||||
ModuleType.ShortcutGuide => GPOWrapper.GetConfiguredShortcutGuideEnabledValue(),
|
||||
ModuleType.PowerOCR => GPOWrapper.GetConfiguredTextExtractorEnabledValue(),
|
||||
ModuleType.ZoomIt => GPOWrapper.GetConfiguredZoomItEnabledValue(),
|
||||
_ => GpoRuleConfigured.Unavailable,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// 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 Microsoft.Windows.ApplicationModel.Resources;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess.Helpers;
|
||||
|
||||
internal static class ResourceLoaderInstance
|
||||
{
|
||||
internal static ResourceLoader ResourceLoader { get; } = new("PowerToys.QuickAccess.pri");
|
||||
}
|
||||
89
src/settings-ui/QuickAccess.UI/PowerToys.QuickAccess.csproj
Normal file
89
src/settings-ui/QuickAccess.UI/PowerToys.QuickAccess.csproj
Normal file
@@ -0,0 +1,89 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\Common.SelfContained.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net9.0-windows10.0.26100.0</TargetFramework>
|
||||
<RootNamespace>Microsoft.PowerToys.QuickAccess</RootNamespace>
|
||||
<AssemblyName>PowerToys.QuickAccess</AssemblyName>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<WindowsPackageType>None</WindowsPackageType>
|
||||
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<OutputPath>..\..\..\$(Platform)\$(Configuration)\WinUI3Apps</OutputPath>
|
||||
<EnableDefaultPageItems>false</EnableDefaultPageItems>
|
||||
<EnableDefaultApplicationDefinition>false</EnableDefaultApplicationDefinition>
|
||||
<Nullable>enable</Nullable>
|
||||
<ProjectPriFileName>PowerToys.QuickAccess.pri</ProjectPriFileName>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<CsWinRTIncludes>PowerToys.GPOWrapper</CsWinRTIncludes>
|
||||
<CsWinRTGeneratedFilesDir>$(OutDir)</CsWinRTGeneratedFilesDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ApplicationDefinition Include="QuickAccessXaml\App.xaml" />
|
||||
<Page Include="QuickAccessXaml\MainWindow.xaml" />
|
||||
<Page Include="QuickAccessXaml\Flyout\ShellPage.xaml" />
|
||||
<Page Include="QuickAccessXaml\Flyout\LaunchPage.xaml" />
|
||||
<Page Include="QuickAccessXaml\Flyout\AppsListPage.xaml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Include="..\Settings.UI\SettingsXAML\Styles\Button.xaml">
|
||||
<Link>Resources\Styles\Button.xaml</Link>
|
||||
</Page>
|
||||
<Page Include="..\Settings.UI\SettingsXAML\Styles\TextBlock.xaml">
|
||||
<Link>Resources\Styles\TextBlock.xaml</Link>
|
||||
</Page>
|
||||
<Page Include="..\Settings.UI\SettingsXAML\Themes\Colors.xaml">
|
||||
<Link>Resources\Themes\Colors.xaml</Link>
|
||||
</Page>
|
||||
<Page Include="..\Settings.UI\SettingsXAML\Themes\Generic.xaml">
|
||||
<Link>Resources\Themes\Generic.xaml</Link>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PRIResource Include="..\Settings.UI\Strings\en-us\Resources.resw">
|
||||
<Link>Strings\en-us\Resources.resw</Link>
|
||||
</PRIResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\Settings.UI\Assets\Settings\Icons\**\*">
|
||||
<Link>Assets\Settings\Icons\%(RecursiveDir)%(Filename)%(Extension)</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Animations" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Segmented" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Converters" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Extensions" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" />
|
||||
<PackageReference Include="WinUIEx" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\common\GPOWrapper\GPOWrapper.vcxproj" />
|
||||
<ProjectReference Include="..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
|
||||
<ProjectReference Include="..\..\common\interop\PowerToys.Interop.vcxproj" />
|
||||
<ProjectReference Include="..\..\common\Common.UI\Common.UI.csproj" />
|
||||
<ProjectReference Include="..\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||
<ProjectReference Include="..\Settings.UI.Controls\Settings.UI.Controls.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Manifest Include="$(ApplicationManifest)" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
62
src/settings-ui/QuickAccess.UI/QuickAccessLaunchContext.cs
Normal file
62
src/settings-ui/QuickAccess.UI/QuickAccessLaunchContext.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess;
|
||||
|
||||
public sealed record QuickAccessLaunchContext(string? ShowEventName, string? ExitEventName, string? RunnerPipeName, string? AppPipeName)
|
||||
{
|
||||
public static QuickAccessLaunchContext Parse(string[] args)
|
||||
{
|
||||
string? showEvent = null;
|
||||
string? exitEvent = null;
|
||||
string? runnerPipe = null;
|
||||
string? appPipe = null;
|
||||
|
||||
foreach (var arg in args)
|
||||
{
|
||||
if (TryReadValue(arg, "--show-event", out var value))
|
||||
{
|
||||
showEvent = value;
|
||||
}
|
||||
else if (TryReadValue(arg, "--exit-event", out value))
|
||||
{
|
||||
exitEvent = value;
|
||||
}
|
||||
else if (TryReadValue(arg, "--runner-pipe", out value))
|
||||
{
|
||||
runnerPipe = value;
|
||||
}
|
||||
else if (TryReadValue(arg, "--app-pipe", out value))
|
||||
{
|
||||
appPipe = value;
|
||||
}
|
||||
}
|
||||
|
||||
return new QuickAccessLaunchContext(showEvent, exitEvent, runnerPipe, appPipe);
|
||||
}
|
||||
|
||||
private static bool TryReadValue(string candidate, string key, [NotNullWhen(true)] out string? value)
|
||||
{
|
||||
if (candidate.StartsWith(key, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (candidate.Length == key.Length)
|
||||
{
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (candidate[key.Length] == '=')
|
||||
{
|
||||
value = candidate[(key.Length + 1)..].Trim('"');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
60
src/settings-ui/QuickAccess.UI/QuickAccessXAML/App.xaml
Normal file
60
src/settings-ui/QuickAccess.UI/QuickAccessXAML/App.xaml
Normal file
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Application
|
||||
x:Class="Microsoft.PowerToys.QuickAccess.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
|
||||
<ResourceDictionary Source="/Resources/Styles/Button.xaml" />
|
||||
<ResourceDictionary Source="/Resources/Styles/TextBlock.xaml" />
|
||||
<ResourceDictionary Source="/Resources/Themes/Colors.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<ResourceDictionary.ThemeDictionaries>
|
||||
<ResourceDictionary x:Key="Default">
|
||||
<SolidColorBrush
|
||||
x:Key="LayerOnAcrylicFillColorDefaultBrush"
|
||||
Opacity="0.7"
|
||||
Color="#FFFFFFFF" />
|
||||
<SolidColorBrush x:Key="CardStrokeColorDefaultBrush" Color="#0F000000" />
|
||||
<SolidColorBrush x:Key="CardBackgroundFillColorDefaultBrush" Color="#B3FFFFFF" />
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Light">
|
||||
<SolidColorBrush
|
||||
x:Key="LayerOnAcrylicFillColorDefaultBrush"
|
||||
Opacity="0.7"
|
||||
Color="#FFFFFFFF" />
|
||||
<SolidColorBrush x:Key="CardStrokeColorDefaultBrush" Color="#0F000000" />
|
||||
<SolidColorBrush x:Key="CardBackgroundFillColorDefaultBrush" Color="#B3FFFFFF" />
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="Dark">
|
||||
<SolidColorBrush
|
||||
x:Key="LayerOnAcrylicFillColorDefaultBrush"
|
||||
Opacity="0.6"
|
||||
Color="#FF000000" />
|
||||
<SolidColorBrush x:Key="CardStrokeColorDefaultBrush" Color="#0FFFFFFF" />
|
||||
<SolidColorBrush x:Key="CardBackgroundFillColorDefaultBrush" Color="#0DFFFFFF" />
|
||||
</ResourceDictionary>
|
||||
<ResourceDictionary x:Key="HighContrast">
|
||||
<SolidColorBrush x:Key="LayerOnAcrylicFillColorDefaultBrush" Color="{ThemeResource SystemColorWindowColor}" />
|
||||
<SolidColorBrush x:Key="CardStrokeColorDefaultBrush" Color="{ThemeResource SystemColorWindowTextColor}" />
|
||||
<SolidColorBrush x:Key="CardBackgroundFillColorDefaultBrush" Color="{ThemeResource SystemColorWindowColor}" />
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary.ThemeDictionaries>
|
||||
|
||||
<tkconverters:BoolToVisibilityConverter
|
||||
x:Key="ReverseBoolToVisibilityConverter"
|
||||
FalseValue="Visible"
|
||||
TrueValue="Collapsed" />
|
||||
<tkconverters:BoolToVisibilityConverter
|
||||
x:Key="BoolToVisibilityConverter"
|
||||
FalseValue="Collapsed"
|
||||
TrueValue="Visible" />
|
||||
<tkconverters:BoolNegationConverter x:Key="BoolNegationConverter" />
|
||||
<tkconverters:StringVisibilityConverter x:Key="StringVisibilityConverter" />
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
36
src/settings-ui/QuickAccess.UI/QuickAccessXAML/App.xaml.cs
Normal file
36
src/settings-ui/QuickAccess.UI/QuickAccessXAML/App.xaml.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess;
|
||||
|
||||
public partial class App : Application
|
||||
{
|
||||
private static MainWindow? _window;
|
||||
|
||||
public App()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override void OnLaunched(LaunchActivatedEventArgs args)
|
||||
{
|
||||
var launchContext = QuickAccessLaunchContext.Parse(Environment.GetCommandLineArgs());
|
||||
_window = new MainWindow(launchContext);
|
||||
_window.Closed += OnWindowClosed;
|
||||
_window.Activate();
|
||||
}
|
||||
|
||||
private static void OnWindowClosed(object sender, WindowEventArgs args)
|
||||
{
|
||||
if (sender is MainWindow window)
|
||||
{
|
||||
window.Closed -= OnWindowClosed;
|
||||
}
|
||||
|
||||
_window = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<Page
|
||||
x:Class="Microsoft.PowerToys.QuickAccess.Flyout.AppsListPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls"
|
||||
xmlns:converters="using:Microsoft.PowerToys.Settings.UI.Controls.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:Microsoft.PowerToys.QuickAccess.Flyout"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:viewModels="using:Microsoft.PowerToys.QuickAccess.ViewModels"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources>
|
||||
<converters:EnumToBooleanConverter x:Key="EnumToBooleanConverter" />
|
||||
</Page.Resources>
|
||||
<Grid Background="{ThemeResource LayerOnAcrylicFillColorDefaultBrush}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid Padding="24,32,24,0">
|
||||
<TextBlock
|
||||
x:Uid="AllAppsTxt"
|
||||
VerticalAlignment="Center"
|
||||
Style="{StaticResource BodyStrongTextBlockStyle}" />
|
||||
<StackPanel
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal"
|
||||
Spacing="8">
|
||||
<Button
|
||||
x:Uid="Dashboard_SortBy"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Style="{StaticResource SubtleButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="Dashboard_SortBy_ToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.Content>
|
||||
<FontIcon FontSize="14" Glyph="" />
|
||||
</Button.Content>
|
||||
<Button.Flyout>
|
||||
<MenuFlyout Placement="BottomEdgeAlignedRight">
|
||||
<ToggleMenuFlyoutItem
|
||||
x:Uid="Dashboard_SortAlphabetical"
|
||||
Click="SortAlphabetical_Click"
|
||||
IsChecked="{x:Bind ViewModel.DashboardSortOrder, Mode=OneWay, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter=Alphabetical}" />
|
||||
<ToggleMenuFlyoutItem
|
||||
x:Uid="Dashboard_SortByStatus"
|
||||
Click="SortByStatus_Click"
|
||||
IsChecked="{x:Bind ViewModel.DashboardSortOrder, Mode=OneWay, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter=ByStatus}" />
|
||||
</MenuFlyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
<Button
|
||||
x:Uid="BackBtn"
|
||||
Padding="8,4,8,4"
|
||||
VerticalAlignment="Center"
|
||||
Click="BackButton_Click">
|
||||
<Button.Content>
|
||||
<StackPanel
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal"
|
||||
Spacing="12">
|
||||
<FontIcon
|
||||
Margin="0,2,0,0"
|
||||
FontSize="12"
|
||||
Glyph="" />
|
||||
<TextBlock x:Uid="BackLabel" Style="{StaticResource CaptionTextBlockStyle}" />
|
||||
</StackPanel>
|
||||
</Button.Content>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Grid Grid.Row="1">
|
||||
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
|
||||
<controls:ModuleList
|
||||
Margin="8,12,12,12"
|
||||
DividerThickness="0,0,0,0"
|
||||
IsItemClickable="False"
|
||||
ItemsSource="{x:Bind ViewModel.FlyoutMenuItems, Mode=OneWay}" />
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Microsoft.PowerToys.QuickAccess.ViewModels;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media.Animation;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess.Flyout;
|
||||
|
||||
public sealed partial class AppsListPage : Page
|
||||
{
|
||||
private FlyoutNavigationContext? _context;
|
||||
|
||||
public AppsListPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public AllAppsViewModel ViewModel { get; private set; } = default!;
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
if (e.Parameter is FlyoutNavigationContext context)
|
||||
{
|
||||
_context = context;
|
||||
ViewModel = context.AllAppsViewModel;
|
||||
DataContext = ViewModel;
|
||||
ViewModel.RefreshSettings();
|
||||
}
|
||||
}
|
||||
|
||||
private void BackButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_context == null || Frame == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Frame.Navigate(typeof(LaunchPage), _context, new SlideNavigationTransitionInfo { Effect = SlideNavigationTransitionEffect.FromLeft });
|
||||
}
|
||||
|
||||
private void SortAlphabetical_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (ViewModel != null)
|
||||
{
|
||||
ViewModel.DashboardSortOrder = DashboardSortOrder.Alphabetical;
|
||||
}
|
||||
}
|
||||
|
||||
private void SortByStatus_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (ViewModel != null)
|
||||
{
|
||||
ViewModel.DashboardSortOrder = DashboardSortOrder.ByStatus;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// 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 Microsoft.PowerToys.QuickAccess.Services;
|
||||
using Microsoft.PowerToys.QuickAccess.ViewModels;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess.Flyout;
|
||||
|
||||
internal sealed record FlyoutNavigationContext(
|
||||
LauncherViewModel LauncherViewModel,
|
||||
AllAppsViewModel AllAppsViewModel,
|
||||
IQuickAccessCoordinator Coordinator);
|
||||
@@ -1,5 +1,5 @@
|
||||
<Page
|
||||
x:Class="Microsoft.PowerToys.Settings.UI.Flyout.LaunchPage"
|
||||
<Page
|
||||
x:Class="Microsoft.PowerToys.QuickAccess.Flyout.LaunchPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:animatedVisuals="using:Microsoft.UI.Xaml.Controls.AnimatedVisuals"
|
||||
@@ -9,7 +9,7 @@
|
||||
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
xmlns:viewModels="using:Microsoft.PowerToys.Settings.UI.ViewModels"
|
||||
xmlns:viewModels="using:Microsoft.PowerToys.QuickAccess.ViewModels"
|
||||
mc:Ignorable="d">
|
||||
<Page.Resources>
|
||||
<Style
|
||||
@@ -52,7 +52,6 @@
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal"
|
||||
Spacing="12">
|
||||
|
||||
<TextBlock x:Uid="MoreLabel" Style="{StaticResource CaptionTextBlockStyle}" />
|
||||
<FontIcon
|
||||
Margin="0,2,0,0"
|
||||
@@ -64,45 +63,12 @@
|
||||
</Grid>
|
||||
<Grid Grid.Row="1">
|
||||
<ScrollViewer>
|
||||
<ItemsControl
|
||||
<controls:QuickAccessList
|
||||
Margin="12,26,12,24"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Top"
|
||||
ItemsSource="{x:Bind ViewModel.FlyoutMenuItems}"
|
||||
TabNavigation="Local">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<tkcontrols:WrapPanel HorizontalAlignment="Stretch" VerticalSpacing="12" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="viewModels:FlyoutMenuItem">
|
||||
<controls:FlyoutMenuButton
|
||||
AutomationProperties.Name="{x:Bind Label}"
|
||||
Click="ModuleButton_Click"
|
||||
Tag="{x:Bind Tag}"
|
||||
Visibility="{x:Bind Visible, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<controls:FlyoutMenuButton.Content>
|
||||
<TextBlock
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind Label}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
</controls:FlyoutMenuButton.Content>
|
||||
<controls:FlyoutMenuButton.Icon>
|
||||
<Image>
|
||||
<Image.Source>
|
||||
<BitmapImage UriSource="{x:Bind Icon, Mode=OneWay}" />
|
||||
</Image.Source>
|
||||
</Image>
|
||||
</controls:FlyoutMenuButton.Icon>
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip Content="{x:Bind ToolTip}" Visibility="{x:Bind ToolTip, Converter={StaticResource StringVisibilityConverter}}" />
|
||||
</ToolTipService.ToolTip>
|
||||
</controls:FlyoutMenuButton>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
TabNavigation="Local" />
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -150,7 +116,7 @@
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="SettingsTooltip" />
|
||||
</ToolTipService.ToolTip>
|
||||
<AnimatedIcon x:Name="SearchAnimatedIcon">
|
||||
<AnimatedIcon x:Name="SettingsAnimatedIcon">
|
||||
<AnimatedIcon.Source>
|
||||
<animatedVisuals:AnimatedSettingsVisualSource />
|
||||
</AnimatedIcon.Source>
|
||||
@@ -159,14 +125,6 @@
|
||||
</AnimatedIcon.FallbackIconSource>
|
||||
</AnimatedIcon>
|
||||
</Button>
|
||||
<!--<AppBarSeparator />
|
||||
<Button
|
||||
x:Name="QuitBtn"
|
||||
Style="{StaticResource FlyoutButtonStyle}"
|
||||
ToolTipService.ToolTip="Quit"
|
||||
Click="QuitButton_Click">
|
||||
<FontIcon FontSize="16" Glyph="" />
|
||||
</Button>-->
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -0,0 +1,80 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.QuickAccess.Services;
|
||||
using Microsoft.PowerToys.QuickAccess.ViewModels;
|
||||
using Microsoft.PowerToys.Settings.UI.Controls;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media.Animation;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using PowerToys.Interop;
|
||||
using Windows.System;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess.Flyout;
|
||||
|
||||
public sealed partial class LaunchPage : Page
|
||||
{
|
||||
private AllAppsViewModel? _allAppsViewModel;
|
||||
private IQuickAccessCoordinator? _coordinator;
|
||||
|
||||
public LaunchPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public LauncherViewModel ViewModel { get; private set; } = default!;
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
if (e.Parameter is FlyoutNavigationContext context)
|
||||
{
|
||||
ViewModel = context.LauncherViewModel;
|
||||
_allAppsViewModel = context.AllAppsViewModel;
|
||||
_coordinator = context.Coordinator;
|
||||
DataContext = ViewModel;
|
||||
}
|
||||
}
|
||||
|
||||
private void SettingsBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_coordinator?.OpenSettings();
|
||||
}
|
||||
|
||||
private async void DocsBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_coordinator == null || !await _coordinator.ShowDocumentationAsync())
|
||||
{
|
||||
await Launcher.LaunchUriAsync(new Uri("https://aka.ms/PowerToysOverview"));
|
||||
}
|
||||
}
|
||||
|
||||
private void AllAppButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (Frame == null || _allAppsViewModel == null || ViewModel == null || _coordinator == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var context = new FlyoutNavigationContext(ViewModel, _allAppsViewModel, _coordinator);
|
||||
Frame.Navigate(typeof(AppsListPage), context, new SlideNavigationTransitionInfo { Effect = SlideNavigationTransitionEffect.FromRight });
|
||||
}
|
||||
|
||||
public void ReportBugBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_coordinator?.ReportBug();
|
||||
}
|
||||
|
||||
private void UpdateInfoBar_Tapped(object sender, TappedRoutedEventArgs e)
|
||||
{
|
||||
_coordinator?.OpenGeneralSettingsForUpdates();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<Page
|
||||
x:Class="Microsoft.PowerToys.Settings.UI.Flyout.ShellPage"
|
||||
<Page
|
||||
x:Class="Microsoft.PowerToys.QuickAccess.Flyout.ShellPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
@@ -0,0 +1,68 @@
|
||||
// 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 Microsoft.PowerToys.QuickAccess.Services;
|
||||
using Microsoft.PowerToys.QuickAccess.ViewModels;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media.Animation;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess.Flyout;
|
||||
|
||||
/// <summary>
|
||||
/// Hosts the flyout navigation frame.
|
||||
/// </summary>
|
||||
public sealed partial class ShellPage : Page
|
||||
{
|
||||
private LauncherViewModel? _launcherViewModel;
|
||||
private AllAppsViewModel? _allAppsViewModel;
|
||||
private IQuickAccessCoordinator? _coordinator;
|
||||
|
||||
public ShellPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public void Initialize(IQuickAccessCoordinator coordinator, LauncherViewModel launcherViewModel, AllAppsViewModel allAppsViewModel)
|
||||
{
|
||||
_coordinator = coordinator;
|
||||
_launcherViewModel = launcherViewModel;
|
||||
_allAppsViewModel = allAppsViewModel;
|
||||
}
|
||||
|
||||
private void Page_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_launcherViewModel == null || _allAppsViewModel == null || _coordinator == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ContentFrame.Content is LaunchPage)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var context = new FlyoutNavigationContext(_launcherViewModel, _allAppsViewModel, _coordinator);
|
||||
ContentFrame.Navigate(typeof(LaunchPage), context, new SuppressNavigationTransitionInfo());
|
||||
}
|
||||
|
||||
internal void NavigateToLaunch()
|
||||
{
|
||||
if (_launcherViewModel == null || _allAppsViewModel == null || _coordinator == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var context = new FlyoutNavigationContext(_launcherViewModel, _allAppsViewModel, _coordinator);
|
||||
ContentFrame.Navigate(typeof(LaunchPage), context, new SlideNavigationTransitionInfo { Effect = SlideNavigationTransitionEffect.FromLeft });
|
||||
}
|
||||
|
||||
internal void RefreshIfAppsList()
|
||||
{
|
||||
if (ContentFrame.Content is AppsListPage appsListPage)
|
||||
{
|
||||
appsListPage.ViewModel?.RefreshSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,16 @@
|
||||
<winuiex:WindowEx
|
||||
x:Class="Microsoft.PowerToys.Settings.UI.FlyoutWindow"
|
||||
<winuiEx:WindowEx
|
||||
x:Class="Microsoft.PowerToys.QuickAccess.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:flyout="using:Microsoft.PowerToys.Settings.UI.Flyout"
|
||||
xmlns:local="using:Microsoft.PowerToys.Settings.UI"
|
||||
xmlns:flyout="using:Microsoft.PowerToys.QuickAccess.Flyout"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:winuiex="using:WinUIEx"
|
||||
Title="PowerToys Settings"
|
||||
xmlns:winuiEx="using:WinUIEx"
|
||||
Title="PowerToys Quick Access (Preview)"
|
||||
Width="400"
|
||||
Height="516"
|
||||
MinWidth="400"
|
||||
MinHeight="516"
|
||||
IsAlwaysOnTop="True"
|
||||
IsMaximizable="False"
|
||||
IsMinimizable="False"
|
||||
@@ -15,8 +18,8 @@
|
||||
IsShownInSwitchers="False"
|
||||
IsTitleBarVisible="False"
|
||||
mc:Ignorable="d">
|
||||
<winuiex:WindowEx.Backdrop>
|
||||
<winuiex:AcrylicSystemBackdrop
|
||||
<winuiEx:WindowEx.Backdrop>
|
||||
<winuiEx:AcrylicSystemBackdrop
|
||||
DarkFallbackColor="#1c1c1c"
|
||||
DarkLuminosityOpacity="0.96"
|
||||
DarkTintColor="#202020"
|
||||
@@ -25,8 +28,9 @@
|
||||
LightLuminosityOpacity="0.90"
|
||||
LightTintColor="#F3F3F3"
|
||||
LightTintOpacity="0" />
|
||||
</winuiex:WindowEx.Backdrop>
|
||||
</winuiEx:WindowEx.Backdrop>
|
||||
|
||||
<Grid>
|
||||
<flyout:ShellPage x:Name="FlyoutShellPage" />
|
||||
<flyout:ShellPage x:Name="ShellHost" />
|
||||
</Grid>
|
||||
</winuiex:WindowEx>
|
||||
</winuiEx:WindowEx>
|
||||
@@ -0,0 +1,732 @@
|
||||
// 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.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.QuickAccess.Services;
|
||||
using Microsoft.PowerToys.QuickAccess.ViewModels;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Windowing;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Windows.Graphics;
|
||||
using WinRT.Interop;
|
||||
using WinUIEx;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess;
|
||||
|
||||
public sealed partial class MainWindow : WindowEx, IDisposable
|
||||
{
|
||||
private readonly QuickAccessLaunchContext _launchContext;
|
||||
private readonly DispatcherQueue _dispatcherQueue;
|
||||
private readonly IntPtr _hwnd;
|
||||
private readonly AppWindow? _appWindow;
|
||||
private readonly LauncherViewModel _launcherViewModel;
|
||||
private readonly AllAppsViewModel _allAppsViewModel;
|
||||
private readonly QuickAccessCoordinator _coordinator;
|
||||
private bool _disposed;
|
||||
private EventWaitHandle? _showEvent;
|
||||
private EventWaitHandle? _exitEvent;
|
||||
private ManualResetEventSlim? _listenerShutdownEvent;
|
||||
private Thread? _showListenerThread;
|
||||
private Thread? _exitListenerThread;
|
||||
private bool _isWindowCloaked;
|
||||
private bool _initialActivationHandled;
|
||||
private bool _isPrimed;
|
||||
|
||||
// Prevent auto-hide until the window actually gained focus once.
|
||||
private bool _hasSeenInteractiveActivation;
|
||||
private bool _isVisible;
|
||||
private IntPtr _mouseHook;
|
||||
private LowLevelMouseProc? _mouseHookDelegate;
|
||||
private CancellationTokenSource? _trimCts;
|
||||
|
||||
private const int DefaultWidth = 320;
|
||||
private const int DefaultHeight = 480;
|
||||
private const int DwmWaCloak = 13;
|
||||
private const int GwlStyle = -16;
|
||||
private const int GwlExStyle = -20;
|
||||
private const int SwHide = 0;
|
||||
private const int SwShow = 5;
|
||||
private const int SwShowNoActivate = 8;
|
||||
private const uint SwpShowWindow = 0x0040;
|
||||
private const uint SwpNoZorder = 0x0004;
|
||||
private const uint SwpNoSize = 0x0001;
|
||||
private const uint SwpNoMove = 0x0002;
|
||||
private const uint SwpNoActivate = 0x0010;
|
||||
private const uint SwpFrameChanged = 0x0020;
|
||||
private const long WsSysmenu = 0x00080000L;
|
||||
private const long WsMinimizeBox = 0x00020000L;
|
||||
private const long WsMaximizeBox = 0x00010000L;
|
||||
private const long WsExToolWindow = 0x00000080L;
|
||||
private const uint MonitorDefaulttonearest = 0x00000002;
|
||||
private static readonly IntPtr HwndTopmost = new(-1);
|
||||
private static readonly IntPtr HwndBottom = new(1);
|
||||
|
||||
public MainWindow(QuickAccessLaunchContext launchContext)
|
||||
{
|
||||
InitializeComponent();
|
||||
_launchContext = launchContext;
|
||||
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
_hwnd = WindowNative.GetWindowHandle(this);
|
||||
_appWindow = InitializeAppWindow(_hwnd);
|
||||
Title = "PowerToys Quick Access (Preview)";
|
||||
|
||||
_coordinator = new QuickAccessCoordinator(this, _launchContext);
|
||||
_launcherViewModel = new LauncherViewModel(_coordinator);
|
||||
_allAppsViewModel = new AllAppsViewModel(_coordinator);
|
||||
ShellHost.Initialize(_coordinator, _launcherViewModel, _allAppsViewModel);
|
||||
|
||||
CustomizeWindowChrome();
|
||||
HideFromTaskbar();
|
||||
HideWindow();
|
||||
InitializeEventListeners();
|
||||
Closed += OnClosed;
|
||||
Activated += OnActivated;
|
||||
}
|
||||
|
||||
private AppWindow? InitializeAppWindow(IntPtr hwnd)
|
||||
{
|
||||
var windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hwnd);
|
||||
return AppWindow.GetFromWindowId(windowId);
|
||||
}
|
||||
|
||||
private void HideWindow()
|
||||
{
|
||||
if (_hwnd != IntPtr.Zero)
|
||||
{
|
||||
var cloaked = CloakWindow();
|
||||
|
||||
if (!ShowWindowNative(_hwnd, SwHide) && _appWindow != null)
|
||||
{
|
||||
_appWindow.Hide();
|
||||
}
|
||||
|
||||
if (cloaked)
|
||||
{
|
||||
ShowWindowNative(_hwnd, SwShowNoActivate);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetWindowPosNative(_hwnd, HwndBottom, 0, 0, 0, 0, SwpNoMove | SwpNoSize | SwpNoActivate);
|
||||
}
|
||||
}
|
||||
else if (_appWindow != null)
|
||||
{
|
||||
_appWindow.Hide();
|
||||
}
|
||||
|
||||
_isVisible = false;
|
||||
RemoveGlobalMouseHook();
|
||||
|
||||
ScheduleMemoryTrim();
|
||||
}
|
||||
|
||||
internal void RequestHide()
|
||||
{
|
||||
if (_dispatcherQueue.HasThreadAccess)
|
||||
{
|
||||
HideWindow();
|
||||
}
|
||||
else
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(HideWindow);
|
||||
}
|
||||
}
|
||||
|
||||
private void ScheduleMemoryTrim()
|
||||
{
|
||||
CancelMemoryTrim();
|
||||
_trimCts = new CancellationTokenSource();
|
||||
var token = _trimCts.Token;
|
||||
|
||||
// Delay the trim to avoid aggressive GC during quick toggles
|
||||
Task.Delay(2000, token).ContinueWith(
|
||||
_ =>
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TrimMemory();
|
||||
},
|
||||
token,
|
||||
TaskContinuationOptions.None,
|
||||
TaskScheduler.Default);
|
||||
}
|
||||
|
||||
private void CancelMemoryTrim()
|
||||
{
|
||||
_trimCts?.Cancel();
|
||||
_trimCts?.Dispose();
|
||||
_trimCts = null;
|
||||
}
|
||||
|
||||
private void TrimMemory()
|
||||
{
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
GC.Collect();
|
||||
SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
|
||||
}
|
||||
|
||||
private void InitializeEventListeners()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_launchContext.ShowEventName))
|
||||
{
|
||||
try
|
||||
{
|
||||
_showEvent = EventWaitHandle.OpenExisting(_launchContext.ShowEventName!);
|
||||
EnsureListenerInfrastructure();
|
||||
StartShowListenerThread();
|
||||
}
|
||||
catch (WaitHandleCannotBeOpenedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(_launchContext.ExitEventName))
|
||||
{
|
||||
try
|
||||
{
|
||||
_exitEvent = EventWaitHandle.OpenExisting(_launchContext.ExitEventName!);
|
||||
EnsureListenerInfrastructure();
|
||||
StartExitListenerThread();
|
||||
}
|
||||
catch (WaitHandleCannotBeOpenedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowWindow()
|
||||
{
|
||||
CancelMemoryTrim();
|
||||
|
||||
if (_hwnd != IntPtr.Zero)
|
||||
{
|
||||
UncloakWindow();
|
||||
|
||||
ShowWindowNative(_hwnd, SwShow);
|
||||
|
||||
var flags = SwpNoSize | SwpShowWindow;
|
||||
var targetX = 0;
|
||||
var targetY = 0;
|
||||
|
||||
var windowSize = _appWindow?.Size;
|
||||
var windowWidth = windowSize?.Width ?? DefaultWidth;
|
||||
var windowHeight = windowSize?.Height ?? DefaultHeight;
|
||||
|
||||
GetCursorPos(out var cursorPosition);
|
||||
var monitorHandle = MonitorFromPointNative(cursorPosition, MonitorDefaulttonearest);
|
||||
if (monitorHandle != IntPtr.Zero)
|
||||
{
|
||||
var monitorInfo = new MonitorInfo { CbSize = Marshal.SizeOf<MonitorInfo>() };
|
||||
if (GetMonitorInfoNative(monitorHandle, ref monitorInfo))
|
||||
{
|
||||
targetX = monitorInfo.RcWork.Right - windowWidth;
|
||||
targetY = monitorInfo.RcWork.Bottom - windowHeight;
|
||||
}
|
||||
}
|
||||
|
||||
SetWindowPosNative(_hwnd, HwndTopmost, targetX, targetY, 0, 0, flags);
|
||||
WindowHelpers.BringToForeground(_hwnd);
|
||||
}
|
||||
|
||||
_hasSeenInteractiveActivation = true;
|
||||
_initialActivationHandled = true;
|
||||
Activate();
|
||||
_isVisible = true;
|
||||
EnsureGlobalMouseHook();
|
||||
ShellHost.RefreshIfAppsList();
|
||||
}
|
||||
|
||||
private void OnActivated(object sender, WindowActivatedEventArgs args)
|
||||
{
|
||||
if (args.WindowActivationState == WindowActivationState.Deactivated)
|
||||
{
|
||||
if (!_hasSeenInteractiveActivation)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HideWindow();
|
||||
return;
|
||||
}
|
||||
|
||||
_hasSeenInteractiveActivation = true;
|
||||
|
||||
if (_initialActivationHandled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_initialActivationHandled = true;
|
||||
PrimeWindow();
|
||||
HideWindow();
|
||||
}
|
||||
|
||||
private void OnClosed(object sender, WindowEventArgs e)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private void PrimeWindow()
|
||||
{
|
||||
if (_isPrimed || _hwnd == IntPtr.Zero)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isPrimed = true;
|
||||
|
||||
if (_appWindow != null)
|
||||
{
|
||||
var currentPosition = _appWindow.Position;
|
||||
_appWindow.MoveAndResize(new RectInt32(currentPosition.X, currentPosition.Y, DefaultWidth, DefaultHeight));
|
||||
}
|
||||
|
||||
// Warm up the window while cloaked so the first summon does not pay XAML initialization cost.
|
||||
var cloaked = CloakWindow();
|
||||
if (cloaked)
|
||||
{
|
||||
ShowWindowNative(_hwnd, SwShowNoActivate);
|
||||
}
|
||||
}
|
||||
|
||||
private void HideFromTaskbar()
|
||||
{
|
||||
if (_appWindow == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_appWindow.IsShownInSwitchers = false;
|
||||
}
|
||||
|
||||
private bool CloakWindow()
|
||||
{
|
||||
if (_hwnd == IntPtr.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_isWindowCloaked)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int cloak = 1;
|
||||
var result = DwmSetWindowAttribute(_hwnd, DwmWaCloak, ref cloak, sizeof(int));
|
||||
if (result == 0)
|
||||
{
|
||||
_isWindowCloaked = true;
|
||||
SetWindowPosNative(_hwnd, HwndBottom, 0, 0, 0, 0, SwpNoMove | SwpNoSize | SwpNoActivate);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void UncloakWindow()
|
||||
{
|
||||
if (_hwnd == IntPtr.Zero || !_isWindowCloaked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int cloak = 0;
|
||||
var result = DwmSetWindowAttribute(_hwnd, DwmWaCloak, ref cloak, sizeof(int));
|
||||
if (result == 0)
|
||||
{
|
||||
_isWindowCloaked = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
StopEventListeners();
|
||||
|
||||
_showEvent?.Dispose();
|
||||
_showEvent = null;
|
||||
|
||||
_exitEvent?.Dispose();
|
||||
_exitEvent = null;
|
||||
|
||||
if (_hwnd != IntPtr.Zero && IsWindow(_hwnd))
|
||||
{
|
||||
UncloakWindow();
|
||||
}
|
||||
|
||||
RemoveGlobalMouseHook();
|
||||
|
||||
_coordinator.Dispose();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool IsWindow(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "ShowWindow", SetLastError = true)]
|
||||
private static extern bool ShowWindowNative(IntPtr hWnd, int nCmdShow);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "GetWindowLongPtr", SetLastError = true)]
|
||||
private static extern nint GetWindowLongPtrNative(IntPtr hWnd, int nIndex);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", SetLastError = true)]
|
||||
private static extern nint SetWindowLongPtrNative(IntPtr hWnd, int nIndex, nint dwNewLong);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "SetWindowPos", SetLastError = true)]
|
||||
private static extern bool SetWindowPosNative(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "SetForegroundWindow", SetLastError = true)]
|
||||
private static extern bool SetForegroundWindowNative(IntPtr hWnd);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "GetForegroundWindow", SetLastError = true)]
|
||||
private static extern IntPtr GetForegroundWindowNative();
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "GetWindowThreadProcessId", SetLastError = true)]
|
||||
private static extern uint GetWindowThreadProcessIdNative(IntPtr hWnd, IntPtr lpdwProcessId);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "AttachThreadInput", SetLastError = true)]
|
||||
private static extern bool AttachThreadInputNative(uint idAttach, uint idAttachTo, bool fAttach);
|
||||
|
||||
[DllImport("dwmapi.dll", EntryPoint = "DwmSetWindowAttribute", SetLastError = true)]
|
||||
private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "MonitorFromPoint", SetLastError = true)]
|
||||
private static extern IntPtr MonitorFromPointNative(NativePoint pt, uint dwFlags);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "GetMonitorInfoW", SetLastError = true)]
|
||||
private static extern bool GetMonitorInfoNative(IntPtr hMonitor, ref MonitorInfo lpmi);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "SetWindowsHookExW", SetLastError = true)]
|
||||
private static extern IntPtr SetWindowsHookExNative(int idHook, LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "UnhookWindowsHookEx", SetLastError = true)]
|
||||
private static extern bool UnhookWindowsHookExNative(IntPtr hhk);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "CallNextHookEx", SetLastError = true)]
|
||||
private static extern IntPtr CallNextHookExNative(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
[DllImport("kernel32.dll", EntryPoint = "GetModuleHandleW", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
private static extern IntPtr GetModuleHandleNative([MarshalAs(UnmanagedType.LPWStr)] string? lpModuleName);
|
||||
|
||||
[DllImport("user32.dll", EntryPoint = "GetWindowRect", SetLastError = true)]
|
||||
private static extern bool GetWindowRectNative(IntPtr hWnd, out Rect rect);
|
||||
|
||||
private void EnsureGlobalMouseHook()
|
||||
{
|
||||
if (_mouseHook != IntPtr.Zero)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_mouseHookDelegate ??= LowLevelMouseHookCallback;
|
||||
var moduleHandle = GetModuleHandleNative(null);
|
||||
_mouseHook = SetWindowsHookExNative(WhMouseLl, _mouseHookDelegate, moduleHandle, 0);
|
||||
}
|
||||
|
||||
private void RemoveGlobalMouseHook()
|
||||
{
|
||||
if (_mouseHook == IntPtr.Zero)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UnhookWindowsHookExNative(_mouseHook);
|
||||
_mouseHook = IntPtr.Zero;
|
||||
}
|
||||
|
||||
private IntPtr LowLevelMouseHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
|
||||
{
|
||||
if (nCode >= 0 && _isVisible && lParam != IntPtr.Zero && IsMouseButtonDownMessage(wParam))
|
||||
{
|
||||
var data = Marshal.PtrToStructure<LowLevelMouseInput>(lParam);
|
||||
if (!IsPointInsideWindow(data.Point))
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
if (_isVisible)
|
||||
{
|
||||
HideWindow();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return CallNextHookExNative(_mouseHook, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
private static bool IsMouseButtonDownMessage(IntPtr wParam)
|
||||
{
|
||||
var message = wParam.ToInt32();
|
||||
return message == WmLbuttondown || message == WmRbuttondown || message == WmMbuttondown || message == WmXbuttondown;
|
||||
}
|
||||
|
||||
private bool IsPointInsideWindow(NativePoint point)
|
||||
{
|
||||
if (_hwnd == IntPtr.Zero)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GetWindowRectNative(_hwnd, out var rect))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return point.X >= rect.Left && point.X <= rect.Right && point.Y >= rect.Top && point.Y <= rect.Bottom;
|
||||
}
|
||||
|
||||
private void EnsureListenerInfrastructure()
|
||||
{
|
||||
_listenerShutdownEvent ??= new ManualResetEventSlim(false);
|
||||
}
|
||||
|
||||
private void StartShowListenerThread()
|
||||
{
|
||||
if (_showEvent == null || _listenerShutdownEvent == null || _showListenerThread != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_showListenerThread = new Thread(ListenForShowEvents)
|
||||
{
|
||||
IsBackground = true,
|
||||
Name = "QuickAccess-ShowEventListener",
|
||||
};
|
||||
_showListenerThread.Start();
|
||||
}
|
||||
|
||||
private void StartExitListenerThread()
|
||||
{
|
||||
if (_exitEvent == null || _listenerShutdownEvent == null || _exitListenerThread != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_exitListenerThread = new Thread(ListenForExitEvents)
|
||||
{
|
||||
IsBackground = true,
|
||||
Name = "QuickAccess-ExitEventListener",
|
||||
};
|
||||
_exitListenerThread.Start();
|
||||
}
|
||||
|
||||
private void ListenForShowEvents()
|
||||
{
|
||||
if (_showEvent == null || _listenerShutdownEvent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var handles = new WaitHandle[] { _showEvent, _listenerShutdownEvent.WaitHandle };
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var index = WaitHandle.WaitAny(handles);
|
||||
if (index == 0)
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(ShowWindow);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
catch (ThreadInterruptedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void ListenForExitEvents()
|
||||
{
|
||||
if (_exitEvent == null || _listenerShutdownEvent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var handles = new WaitHandle[] { _exitEvent, _listenerShutdownEvent.WaitHandle };
|
||||
try
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var index = WaitHandle.WaitAny(handles);
|
||||
if (index == 0)
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(Close);
|
||||
break;
|
||||
}
|
||||
|
||||
if (index == 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
catch (ThreadInterruptedException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void StopEventListeners()
|
||||
{
|
||||
if (_listenerShutdownEvent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_listenerShutdownEvent.Set();
|
||||
|
||||
JoinListenerThread(ref _showListenerThread);
|
||||
JoinListenerThread(ref _exitListenerThread);
|
||||
|
||||
_listenerShutdownEvent.Dispose();
|
||||
_listenerShutdownEvent = null;
|
||||
}
|
||||
|
||||
private static void JoinListenerThread(ref Thread? thread)
|
||||
{
|
||||
if (thread == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (!thread.Join(TimeSpan.FromMilliseconds(250)))
|
||||
{
|
||||
thread.Interrupt();
|
||||
thread.Join(TimeSpan.FromMilliseconds(250));
|
||||
}
|
||||
}
|
||||
catch (ThreadInterruptedException)
|
||||
{
|
||||
}
|
||||
catch (ThreadStateException)
|
||||
{
|
||||
}
|
||||
|
||||
thread = null;
|
||||
}
|
||||
|
||||
private void CustomizeWindowChrome()
|
||||
{
|
||||
if (_hwnd == IntPtr.Zero)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var windowAttributesChanged = false;
|
||||
|
||||
var stylePtr = GetWindowLongPtrNative(_hwnd, GwlStyle);
|
||||
var styleError = Marshal.GetLastWin32Error();
|
||||
if (!(stylePtr == nint.Zero && styleError != 0))
|
||||
{
|
||||
var styleValue = (long)stylePtr;
|
||||
var newStyleValue = styleValue & ~(WsSysmenu | WsMinimizeBox | WsMaximizeBox);
|
||||
|
||||
if (newStyleValue != styleValue)
|
||||
{
|
||||
SetWindowLongPtrNative(_hwnd, GwlStyle, (nint)newStyleValue);
|
||||
windowAttributesChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
var exStylePtr = GetWindowLongPtrNative(_hwnd, GwlExStyle);
|
||||
var exStyleError = Marshal.GetLastWin32Error();
|
||||
if (!(exStylePtr == nint.Zero && exStyleError != 0))
|
||||
{
|
||||
var exStyleValue = (long)exStylePtr;
|
||||
var newExStyleValue = exStyleValue | WsExToolWindow;
|
||||
if (newExStyleValue != exStyleValue)
|
||||
{
|
||||
SetWindowLongPtrNative(_hwnd, GwlExStyle, (nint)newExStyleValue);
|
||||
windowAttributesChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (windowAttributesChanged)
|
||||
{
|
||||
// Apply the new chrome immediately so caption buttons disappear right away and the tool-window flag takes effect.
|
||||
SetWindowPosNative(_hwnd, IntPtr.Zero, 0, 0, 0, 0, SwpNoMove | SwpNoSize | SwpNoZorder | SwpNoActivate | SwpFrameChanged);
|
||||
}
|
||||
}
|
||||
|
||||
private const int WhMouseLl = 14;
|
||||
private const int WmLbuttondown = 0x0201;
|
||||
private const int WmRbuttondown = 0x0204;
|
||||
private const int WmMbuttondown = 0x0207;
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern bool GetCursorPos(out NativePoint lpPoint);
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern bool SetProcessWorkingSetSize(IntPtr hProcess, int dwMinimumWorkingSetSize, int dwMaximumWorkingSetSize);
|
||||
|
||||
private const int WmXbuttondown = 0x020B;
|
||||
|
||||
private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
private struct Rect
|
||||
{
|
||||
public int Left;
|
||||
public int Top;
|
||||
public int Right;
|
||||
public int Bottom;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct LowLevelMouseInput
|
||||
{
|
||||
public NativePoint Point;
|
||||
public int MouseData;
|
||||
public int Flags;
|
||||
public int Time;
|
||||
public IntPtr DwExtraInfo;
|
||||
}
|
||||
|
||||
private struct NativePoint
|
||||
{
|
||||
public int X;
|
||||
public int Y;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
private struct MonitorInfo
|
||||
{
|
||||
public int CbSize;
|
||||
public Rect RcMonitor;
|
||||
public Rect RcWork;
|
||||
public uint DwFlags;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// 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.Threading.Tasks;
|
||||
using ManagedCommon;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess.Services;
|
||||
|
||||
public interface IQuickAccessCoordinator
|
||||
{
|
||||
bool IsRunnerElevated { get; }
|
||||
|
||||
void HideFlyout();
|
||||
|
||||
void OpenSettings();
|
||||
|
||||
void OpenGeneralSettingsForUpdates();
|
||||
|
||||
Task<bool> ShowDocumentationAsync();
|
||||
|
||||
void NotifyUserSettingsInteraction();
|
||||
|
||||
bool UpdateModuleEnabled(ModuleType moduleType, bool isEnabled);
|
||||
|
||||
void ReportBug();
|
||||
|
||||
void OnModuleLaunched(ModuleType moduleType);
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Common.UI;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.QuickAccess.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
||||
using PowerToys.Interop;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess.Services;
|
||||
|
||||
internal sealed class QuickAccessCoordinator : IQuickAccessCoordinator, IDisposable
|
||||
{
|
||||
private readonly MainWindow _window;
|
||||
private readonly QuickAccessLaunchContext _launchContext;
|
||||
private readonly SettingsUtils _settingsUtils = SettingsUtils.Default;
|
||||
private readonly object _generalSettingsLock = new();
|
||||
private readonly object _ipcLock = new();
|
||||
private TwoWayPipeMessageIPCManaged? _ipcManager;
|
||||
private bool _ipcUnavailableLogged;
|
||||
|
||||
public QuickAccessCoordinator(MainWindow window, QuickAccessLaunchContext launchContext)
|
||||
{
|
||||
_window = window;
|
||||
_launchContext = launchContext;
|
||||
InitializeIpc();
|
||||
}
|
||||
|
||||
public bool IsRunnerElevated => false; // TODO: wire up real elevation state.
|
||||
|
||||
public void HideFlyout()
|
||||
{
|
||||
_window.RequestHide();
|
||||
}
|
||||
|
||||
public void OpenSettings()
|
||||
{
|
||||
Common.UI.SettingsDeepLink.OpenSettings(Common.UI.SettingsDeepLink.SettingsWindow.Dashboard);
|
||||
_window.RequestHide();
|
||||
}
|
||||
|
||||
public void OpenGeneralSettingsForUpdates()
|
||||
{
|
||||
Common.UI.SettingsDeepLink.OpenSettings(Common.UI.SettingsDeepLink.SettingsWindow.Overview);
|
||||
_window.RequestHide();
|
||||
}
|
||||
|
||||
public Task<bool> ShowDocumentationAsync()
|
||||
{
|
||||
Logger.LogInfo("QuickAccessCoordinator.ShowDocumentationAsync is not yet connected.");
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
public void NotifyUserSettingsInteraction()
|
||||
{
|
||||
Logger.LogDebug("QuickAccessCoordinator.NotifyUserSettingsInteraction invoked.");
|
||||
}
|
||||
|
||||
public bool UpdateModuleEnabled(ModuleType moduleType, bool isEnabled)
|
||||
{
|
||||
GeneralSettings? updatedSettings = null;
|
||||
lock (_generalSettingsLock)
|
||||
{
|
||||
var repository = SettingsRepository<GeneralSettings>.GetInstance(_settingsUtils);
|
||||
var generalSettings = repository.SettingsConfig;
|
||||
var current = ModuleHelper.GetIsModuleEnabled(generalSettings, moduleType);
|
||||
if (current == isEnabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ModuleHelper.SetIsModuleEnabled(generalSettings, moduleType, isEnabled);
|
||||
_settingsUtils.SaveSettings(generalSettings.ToJsonString());
|
||||
Logger.LogInfo($"QuickAccess updated module '{moduleType}' enabled state to {isEnabled}.");
|
||||
updatedSettings = generalSettings;
|
||||
}
|
||||
|
||||
if (updatedSettings != null)
|
||||
{
|
||||
SendGeneralSettingsUpdate(updatedSettings);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ReportBug()
|
||||
{
|
||||
if (!TrySendIpcMessage("{\"bugreport\": 0 }", "bug report request"))
|
||||
{
|
||||
Logger.LogWarning("QuickAccessCoordinator: failed to dispatch bug report request; IPC unavailable.");
|
||||
}
|
||||
}
|
||||
|
||||
public void OnModuleLaunched(ModuleType moduleType)
|
||||
{
|
||||
Logger.LogInfo($"QuickAccessLauncher invoked module {moduleType}.");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DisposeIpc();
|
||||
}
|
||||
|
||||
private void InitializeIpc()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_launchContext.RunnerPipeName) || string.IsNullOrEmpty(_launchContext.AppPipeName))
|
||||
{
|
||||
Logger.LogWarning("QuickAccessCoordinator: IPC pipe names not provided. Runner will not receive updates.");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_ipcManager = new TwoWayPipeMessageIPCManaged(_launchContext.AppPipeName, _launchContext.RunnerPipeName, OnIpcMessageReceived);
|
||||
_ipcManager.Start();
|
||||
_ipcUnavailableLogged = false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("QuickAccessCoordinator: failed to start IPC channel to runner.", ex);
|
||||
DisposeIpc();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnIpcMessageReceived(string message)
|
||||
{
|
||||
Logger.LogDebug($"QuickAccessCoordinator received IPC payload: {message}");
|
||||
}
|
||||
|
||||
private void SendGeneralSettingsUpdate(GeneralSettings updatedSettings)
|
||||
{
|
||||
string payload;
|
||||
try
|
||||
{
|
||||
payload = new OutGoingGeneralSettings(updatedSettings).ToString();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("QuickAccessCoordinator: failed to serialize general settings payload.", ex);
|
||||
return;
|
||||
}
|
||||
|
||||
TrySendIpcMessage(payload, "general settings update");
|
||||
}
|
||||
|
||||
private bool TrySendIpcMessage(string payload, string operationDescription)
|
||||
{
|
||||
lock (_ipcLock)
|
||||
{
|
||||
if (_ipcManager == null)
|
||||
{
|
||||
if (!_ipcUnavailableLogged)
|
||||
{
|
||||
_ipcUnavailableLogged = true;
|
||||
Logger.LogWarning($"QuickAccessCoordinator: unable to send {operationDescription} because IPC is not available.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_ipcManager.Send(payload);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"QuickAccessCoordinator: failed to send {operationDescription}.", ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DisposeIpc()
|
||||
{
|
||||
lock (_ipcLock)
|
||||
{
|
||||
if (_ipcManager == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_ipcManager.End();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning($"QuickAccessCoordinator: exception while shutting down IPC. {ex.Message}");
|
||||
}
|
||||
|
||||
_ipcManager.Dispose();
|
||||
_ipcManager = null;
|
||||
_ipcUnavailableLogged = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Threading;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Controls;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.Interop;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess.Services
|
||||
{
|
||||
public class QuickAccessLauncher : Microsoft.PowerToys.Settings.UI.Controls.QuickAccessLauncher
|
||||
{
|
||||
private readonly IQuickAccessCoordinator? _coordinator;
|
||||
|
||||
public QuickAccessLauncher(IQuickAccessCoordinator? coordinator)
|
||||
: base(coordinator?.IsRunnerElevated ?? false)
|
||||
{
|
||||
_coordinator = coordinator;
|
||||
}
|
||||
|
||||
public override bool Launch(ModuleType moduleType)
|
||||
{
|
||||
bool moduleRun = base.Launch(moduleType);
|
||||
|
||||
if (moduleRun)
|
||||
{
|
||||
_coordinator?.OnModuleLaunched(moduleType);
|
||||
}
|
||||
|
||||
_coordinator?.HideFlyout();
|
||||
|
||||
return moduleRun;
|
||||
}
|
||||
}
|
||||
}
|
||||
172
src/settings-ui/QuickAccess.UI/ViewModels/AllAppsViewModel.cs
Normal file
172
src/settings-ui/QuickAccess.UI/ViewModels/AllAppsViewModel.cs
Normal file
@@ -0,0 +1,172 @@
|
||||
// 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.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using global::PowerToys.GPOWrapper;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.QuickAccess.Helpers;
|
||||
using Microsoft.PowerToys.QuickAccess.Services;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.ViewModels.Commands;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.Windows.ApplicationModel.Resources;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess.ViewModels;
|
||||
|
||||
public sealed class AllAppsViewModel : Observable
|
||||
{
|
||||
private readonly IQuickAccessCoordinator _coordinator;
|
||||
private readonly ISettingsRepository<GeneralSettings> _settingsRepository;
|
||||
private readonly SettingsUtils _settingsUtils;
|
||||
private readonly ResourceLoader _resourceLoader;
|
||||
private readonly DispatcherQueue _dispatcherQueue;
|
||||
private GeneralSettings _generalSettings;
|
||||
|
||||
public ObservableCollection<FlyoutMenuItem> FlyoutMenuItems { get; }
|
||||
|
||||
public DashboardSortOrder DashboardSortOrder
|
||||
{
|
||||
get => _generalSettings.DashboardSortOrder;
|
||||
set
|
||||
{
|
||||
if (_generalSettings.DashboardSortOrder != value)
|
||||
{
|
||||
_generalSettings.DashboardSortOrder = value;
|
||||
_settingsUtils.SaveSettings(_generalSettings.ToJsonString(), _generalSettings.GetModuleName());
|
||||
OnPropertyChanged();
|
||||
RefreshFlyoutMenuItems();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AllAppsViewModel(IQuickAccessCoordinator coordinator)
|
||||
{
|
||||
_coordinator = coordinator;
|
||||
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
_settingsUtils = SettingsUtils.Default;
|
||||
_settingsRepository = SettingsRepository<GeneralSettings>.GetInstance(_settingsUtils);
|
||||
_generalSettings = _settingsRepository.SettingsConfig;
|
||||
_generalSettings.AddEnabledModuleChangeNotification(ModuleEnabledChangedOnSettingsPage);
|
||||
_settingsRepository.SettingsChanged += OnSettingsChanged;
|
||||
|
||||
_resourceLoader = Helpers.ResourceLoaderInstance.ResourceLoader;
|
||||
FlyoutMenuItems = new ObservableCollection<FlyoutMenuItem>();
|
||||
|
||||
RefreshFlyoutMenuItems();
|
||||
}
|
||||
|
||||
private void OnSettingsChanged(GeneralSettings newSettings)
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
_generalSettings = newSettings;
|
||||
_generalSettings.AddEnabledModuleChangeNotification(ModuleEnabledChangedOnSettingsPage);
|
||||
OnPropertyChanged(nameof(DashboardSortOrder));
|
||||
RefreshFlyoutMenuItems();
|
||||
});
|
||||
}
|
||||
|
||||
public void RefreshSettings()
|
||||
{
|
||||
if (_settingsRepository.ReloadSettings())
|
||||
{
|
||||
OnSettingsChanged(_settingsRepository.SettingsConfig);
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshFlyoutMenuItems()
|
||||
{
|
||||
var desiredItems = new List<FlyoutMenuItem>();
|
||||
|
||||
foreach (ModuleType moduleType in Enum.GetValues<ModuleType>())
|
||||
{
|
||||
if (moduleType == ModuleType.GeneralSettings)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var gpo = Helpers.ModuleGpoHelper.GetModuleGpoConfiguration(moduleType);
|
||||
var isLocked = gpo is GpoRuleConfigured.Enabled or GpoRuleConfigured.Disabled;
|
||||
var isEnabled = gpo == GpoRuleConfigured.Enabled || (!isLocked && Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetIsModuleEnabled(_generalSettings, moduleType));
|
||||
|
||||
var existingItem = FlyoutMenuItems.FirstOrDefault(x => x.Tag == moduleType);
|
||||
|
||||
if (existingItem != null)
|
||||
{
|
||||
existingItem.Label = _resourceLoader.GetString(Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetModuleLabelResourceName(moduleType));
|
||||
existingItem.IsLocked = isLocked;
|
||||
existingItem.Icon = Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetModuleTypeFluentIconName(moduleType);
|
||||
|
||||
if (existingItem.IsEnabled != isEnabled)
|
||||
{
|
||||
var callback = existingItem.EnabledChangedCallback;
|
||||
existingItem.EnabledChangedCallback = null;
|
||||
existingItem.IsEnabled = isEnabled;
|
||||
existingItem.EnabledChangedCallback = callback;
|
||||
}
|
||||
|
||||
desiredItems.Add(existingItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
desiredItems.Add(new FlyoutMenuItem
|
||||
{
|
||||
Label = _resourceLoader.GetString(Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetModuleLabelResourceName(moduleType)),
|
||||
IsEnabled = isEnabled,
|
||||
IsLocked = isLocked,
|
||||
Tag = moduleType,
|
||||
Icon = Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetModuleTypeFluentIconName(moduleType),
|
||||
EnabledChangedCallback = EnabledChangedOnUI,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var sortedItems = DashboardSortOrder switch
|
||||
{
|
||||
DashboardSortOrder.ByStatus => desiredItems.OrderByDescending(x => x.IsEnabled).ThenBy(x => x.Label).ToList(),
|
||||
_ => desiredItems.OrderBy(x => x.Label).ToList(),
|
||||
};
|
||||
|
||||
for (int i = FlyoutMenuItems.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (!sortedItems.Contains(FlyoutMenuItems[i]))
|
||||
{
|
||||
FlyoutMenuItems.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < sortedItems.Count; i++)
|
||||
{
|
||||
var item = sortedItems[i];
|
||||
var oldIndex = FlyoutMenuItems.IndexOf(item);
|
||||
|
||||
if (oldIndex < 0)
|
||||
{
|
||||
FlyoutMenuItems.Insert(i, item);
|
||||
}
|
||||
else if (oldIndex != i)
|
||||
{
|
||||
FlyoutMenuItems.Move(oldIndex, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EnabledChangedOnUI(FlyoutMenuItem item)
|
||||
{
|
||||
if (_coordinator.UpdateModuleEnabled(item.Tag, item.IsEnabled))
|
||||
{
|
||||
_coordinator.NotifyUserSettingsInteraction();
|
||||
}
|
||||
}
|
||||
|
||||
private void ModuleEnabledChangedOnSettingsPage()
|
||||
{
|
||||
RefreshFlyoutMenuItems();
|
||||
}
|
||||
}
|
||||
52
src/settings-ui/QuickAccess.UI/ViewModels/FlyoutMenuItem.cs
Normal file
52
src/settings-ui/QuickAccess.UI/ViewModels/FlyoutMenuItem.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Controls;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess.ViewModels;
|
||||
|
||||
public sealed class FlyoutMenuItem : ModuleListItem
|
||||
{
|
||||
private bool _visible;
|
||||
|
||||
public string ToolTip { get; set; } = string.Empty;
|
||||
|
||||
public new ModuleType Tag
|
||||
{
|
||||
get => (ModuleType)(base.Tag ?? ModuleType.PowerLauncher);
|
||||
set => base.Tag = value;
|
||||
}
|
||||
|
||||
public override bool IsEnabled
|
||||
{
|
||||
get => base.IsEnabled;
|
||||
set
|
||||
{
|
||||
if (base.IsEnabled != value)
|
||||
{
|
||||
base.IsEnabled = value;
|
||||
EnabledChangedCallback?.Invoke(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Action<FlyoutMenuItem>? EnabledChangedCallback { get; set; }
|
||||
|
||||
public bool Visible
|
||||
{
|
||||
get => _visible;
|
||||
set
|
||||
{
|
||||
if (_visible != value)
|
||||
{
|
||||
_visible = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// 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.Collections.ObjectModel;
|
||||
using global::PowerToys.GPOWrapper;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.QuickAccess.Services;
|
||||
using Microsoft.PowerToys.Settings.UI.Controls;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.Windows.ApplicationModel.Resources;
|
||||
|
||||
namespace Microsoft.PowerToys.QuickAccess.ViewModels;
|
||||
|
||||
public sealed class LauncherViewModel : Observable
|
||||
{
|
||||
private readonly IQuickAccessCoordinator _coordinator;
|
||||
private readonly ISettingsRepository<GeneralSettings> _settingsRepository;
|
||||
private readonly ResourceLoader _resourceLoader;
|
||||
private readonly DispatcherQueue _dispatcherQueue;
|
||||
private readonly QuickAccessViewModel _quickAccessViewModel;
|
||||
|
||||
public ObservableCollection<QuickAccessItem> FlyoutMenuItems => _quickAccessViewModel.Items;
|
||||
|
||||
public bool IsUpdateAvailable { get; private set; }
|
||||
|
||||
public LauncherViewModel(IQuickAccessCoordinator coordinator)
|
||||
{
|
||||
_coordinator = coordinator;
|
||||
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
var settingsUtils = SettingsUtils.Default;
|
||||
_settingsRepository = SettingsRepository<GeneralSettings>.GetInstance(settingsUtils);
|
||||
|
||||
_resourceLoader = Helpers.ResourceLoaderInstance.ResourceLoader;
|
||||
|
||||
_quickAccessViewModel = new QuickAccessViewModel(
|
||||
_settingsRepository,
|
||||
new Microsoft.PowerToys.QuickAccess.Services.QuickAccessLauncher(_coordinator),
|
||||
moduleType => Helpers.ModuleGpoHelper.GetModuleGpoConfiguration(moduleType) == GpoRuleConfigured.Disabled,
|
||||
_resourceLoader);
|
||||
var updatingSettings = UpdatingSettings.LoadSettings() ?? new UpdatingSettings();
|
||||
IsUpdateAvailable = updatingSettings.State is UpdatingSettings.UpdatingState.ReadyToInstall or UpdatingSettings.UpdatingState.ReadyToDownload;
|
||||
}
|
||||
}
|
||||
16
src/settings-ui/QuickAccess.UI/app.manifest
Normal file
16
src/settings-ui/QuickAccess.UI/app.manifest
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="PowerToys.QuickAccess.app" />
|
||||
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Converters
|
||||
namespace Microsoft.PowerToys.Settings.UI.Controls.Converters
|
||||
{
|
||||
public partial class EnumToBooleanConverter : IValueConverter
|
||||
{
|
||||
@@ -20,7 +20,7 @@ namespace Microsoft.PowerToys.Settings.UI.Converters
|
||||
var enumString = value.ToString();
|
||||
var parameterString = parameter.ToString();
|
||||
|
||||
return enumString.Equals(parameterString, StringComparison.OrdinalIgnoreCase);
|
||||
return enumString!.Equals(parameterString, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Controls.Converters
|
||||
{
|
||||
public partial class ModuleListSortOptionToBooleanConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is ModuleListSortOption sortOption && parameter is string paramString)
|
||||
{
|
||||
if (Enum.TryParse(typeof(ModuleListSortOption), paramString, out object? result) && result != null)
|
||||
{
|
||||
return sortOption == (ModuleListSortOption)result;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is bool isChecked && isChecked && parameter is string paramString)
|
||||
{
|
||||
if (Enum.TryParse(typeof(ModuleListSortOption), paramString, out object? result) && result != null)
|
||||
{
|
||||
return (ModuleListSortOption)result;
|
||||
}
|
||||
}
|
||||
|
||||
return ModuleListSortOption.Alphabetical;
|
||||
}
|
||||
}
|
||||
}
|
||||
107
src/settings-ui/Settings.UI.Controls/ModuleList/ModuleList.xaml
Normal file
107
src/settings-ui/Settings.UI.Controls/ModuleList/ModuleList.xaml
Normal file
@@ -0,0 +1,107 @@
|
||||
<UserControl
|
||||
x:Class="Microsoft.PowerToys.Settings.UI.Controls.ModuleList"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls"
|
||||
xmlns:converters="using:Microsoft.PowerToys.Settings.UI.Controls.Converters"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:tk="using:CommunityToolkit.WinUI"
|
||||
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<UserControl.Resources>
|
||||
<converters:ModuleListSortOptionToBooleanConverter x:Key="ModuleListSortOptionToBooleanConverter" />
|
||||
<x:Double x:Key="SettingsCardWrapThreshold">0</x:Double>
|
||||
<tkconverters:BoolToVisibilityConverter
|
||||
x:Key="BoolToVisibilityConverter"
|
||||
FalseValue="Collapsed"
|
||||
TrueValue="Visible" />
|
||||
<tkconverters:BoolToVisibilityConverter
|
||||
x:Key="ReverseBoolToVisibilityConverter"
|
||||
FalseValue="Visible"
|
||||
TrueValue="Collapsed" />
|
||||
<tkconverters:BoolNegationConverter x:Key="BoolNegationConverter" />
|
||||
|
||||
<Style x:Key="NewInfoBadgeStyle" TargetType="InfoBadge">
|
||||
<Setter Property="Padding" Value="5,1,5,2" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="InfoBadge">
|
||||
<Border
|
||||
x:Name="RootGrid"
|
||||
Padding="{TemplateBinding Padding}"
|
||||
Background="{TemplateBinding Background}"
|
||||
CornerRadius="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.InfoBadgeCornerRadius}">
|
||||
<TextBlock
|
||||
x:Uid="NewInfoBadge"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="10" />
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
<ItemsRepeater x:Name="DashboardView" ItemsSource="{x:Bind ItemsSource, Mode=OneWay}">
|
||||
<ItemsRepeater.Layout>
|
||||
<StackLayout Orientation="Vertical" Spacing="0" />
|
||||
</ItemsRepeater.Layout>
|
||||
<ItemsRepeater.ItemTemplate>
|
||||
<DataTemplate x:DataType="controls:ModuleListItem">
|
||||
<tkcontrols:SettingsCard
|
||||
MinWidth="0"
|
||||
MinHeight="0"
|
||||
Padding="12,4,12,4"
|
||||
tk:FrameworkElementExtensions.AncestorType="controls:ModuleList"
|
||||
Background="Transparent"
|
||||
BorderBrush="{ThemeResource DividerStrokeColorDefaultBrush}"
|
||||
BorderThickness="{Binding (tk:FrameworkElementExtensions.Ancestor).DividerThickness, RelativeSource={RelativeSource Self}}"
|
||||
Click="OnSettingsCardClick"
|
||||
CornerRadius="0"
|
||||
IsClickEnabled="{Binding (tk:FrameworkElementExtensions.Ancestor).IsItemClickable, RelativeSource={RelativeSource Self}}"
|
||||
Tag="{x:Bind}">
|
||||
<tkcontrols:SettingsCard.Header>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="{x:Bind Label, Mode=OneWay}" />
|
||||
<!-- InfoBadge -->
|
||||
<InfoBadge
|
||||
x:Name="NewInfoBadge"
|
||||
Margin="4,0,0,0"
|
||||
Style="{StaticResource NewInfoBadgeStyle}"
|
||||
Visibility="{x:Bind IsNew, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
<FontIcon
|
||||
Width="20"
|
||||
Margin="4,0,0,0"
|
||||
FontSize="16"
|
||||
Glyph=""
|
||||
Visibility="{x:Bind IsLocked, Converter={StaticResource ReverseBoolToVisibilityConverter}, ConverterParameter=True}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="GPOWarning" TextWrapping="WrapWholeWords" />
|
||||
</ToolTipService.ToolTip>
|
||||
</FontIcon>
|
||||
</StackPanel>
|
||||
</tkcontrols:SettingsCard.Header>
|
||||
|
||||
<tkcontrols:SettingsCard.HeaderIcon>
|
||||
<ImageIcon>
|
||||
<ImageIcon.Source>
|
||||
<BitmapImage UriSource="{x:Bind Icon, Mode=OneWay}" />
|
||||
</ImageIcon.Source>
|
||||
</ImageIcon>
|
||||
</tkcontrols:SettingsCard.HeaderIcon>
|
||||
|
||||
<ToggleSwitch
|
||||
HorizontalAlignment="Right"
|
||||
AutomationProperties.Name="{x:Bind Label, Mode=OneWay}"
|
||||
IsEnabled="{x:Bind IsLocked, Converter={StaticResource BoolNegationConverter}, ConverterParameter=True, Mode=OneWay}"
|
||||
IsOn="{x:Bind IsEnabled, Mode=TwoWay}"
|
||||
OffContent=""
|
||||
OnContent="" />
|
||||
</tkcontrols:SettingsCard>
|
||||
</DataTemplate>
|
||||
</ItemsRepeater.ItemTemplate>
|
||||
</ItemsRepeater>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,57 @@
|
||||
// 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 Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Controls
|
||||
{
|
||||
public sealed partial class ModuleList : UserControl
|
||||
{
|
||||
public ModuleList()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
public Thickness DividerThickness
|
||||
{
|
||||
get => (Thickness)GetValue(DividerThicknessProperty);
|
||||
set => SetValue(DividerThicknessProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty DividerThicknessProperty = DependencyProperty.Register(nameof(DividerThickness), typeof(Thickness), typeof(ModuleList), new PropertyMetadata(new Thickness(0, 1, 0, 0)));
|
||||
|
||||
public bool IsItemClickable
|
||||
{
|
||||
get => (bool)GetValue(IsItemClickableProperty);
|
||||
set => SetValue(IsItemClickableProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty IsItemClickableProperty = DependencyProperty.Register(nameof(IsItemClickable), typeof(bool), typeof(ModuleList), new PropertyMetadata(true));
|
||||
|
||||
public object ItemsSource
|
||||
{
|
||||
get => (object)GetValue(ItemsSourceProperty);
|
||||
set => SetValue(ItemsSourceProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(nameof(ItemsSource), typeof(object), typeof(ModuleList), new PropertyMetadata(null));
|
||||
|
||||
public ModuleListSortOption SortOption
|
||||
{
|
||||
get => (ModuleListSortOption)GetValue(SortOptionProperty);
|
||||
set => SetValue(SortOptionProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty SortOptionProperty = DependencyProperty.Register(nameof(SortOption), typeof(ModuleListSortOption), typeof(ModuleList), new PropertyMetadata(ModuleListSortOption.Alphabetical));
|
||||
|
||||
private void OnSettingsCardClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is FrameworkElement element && element.Tag is ModuleListItem item)
|
||||
{
|
||||
item.ClickCommand?.Execute(item.Tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Controls
|
||||
{
|
||||
public class ModuleListItem : INotifyPropertyChanged
|
||||
{
|
||||
private bool _isEnabled;
|
||||
private string _label = string.Empty;
|
||||
private string _icon = string.Empty;
|
||||
private bool _isNew;
|
||||
private bool _isLocked;
|
||||
private object? _tag;
|
||||
private ICommand? _clickCommand;
|
||||
|
||||
public virtual string Label
|
||||
{
|
||||
get => _label;
|
||||
set
|
||||
{
|
||||
if (_label != value)
|
||||
{
|
||||
_label = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string Icon
|
||||
{
|
||||
get => _icon;
|
||||
set
|
||||
{
|
||||
if (_icon != value)
|
||||
{
|
||||
_icon = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsNew
|
||||
{
|
||||
get => _isNew;
|
||||
set
|
||||
{
|
||||
if (_isNew != value)
|
||||
{
|
||||
_isNew = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsLocked
|
||||
{
|
||||
get => _isLocked;
|
||||
set
|
||||
{
|
||||
if (_isLocked != value)
|
||||
{
|
||||
_isLocked = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool IsEnabled
|
||||
{
|
||||
get => _isEnabled;
|
||||
set
|
||||
{
|
||||
if (_isEnabled != value)
|
||||
{
|
||||
_isEnabled = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual object? Tag
|
||||
{
|
||||
get => _tag;
|
||||
set
|
||||
{
|
||||
if (_tag != value)
|
||||
{
|
||||
_tag = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual ICommand? ClickCommand
|
||||
{
|
||||
get => _clickCommand;
|
||||
set
|
||||
{
|
||||
if (_clickCommand != value)
|
||||
{
|
||||
_clickCommand = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Controls
|
||||
{
|
||||
public enum ModuleListSortOption
|
||||
{
|
||||
Alphabetical,
|
||||
ByStatus,
|
||||
}
|
||||
}
|
||||
@@ -21,11 +21,11 @@
|
||||
BorderThickness="{x:Bind BorderThickness, Mode=OneWay}"
|
||||
CornerRadius="{x:Bind CornerRadius, Mode=OneWay}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" MinHeight="44" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid x:Name="TitleGrid">
|
||||
<Grid x:Name="TitleGrid" MinHeight="44">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
@@ -11,9 +11,9 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
|
||||
{
|
||||
public static readonly DependencyProperty TitleContentProperty = DependencyProperty.Register(nameof(TitleContent), typeof(object), typeof(Card), new PropertyMetadata(defaultValue: null, OnVisualPropertyChanged));
|
||||
|
||||
public object TitleContent
|
||||
public object? TitleContent
|
||||
{
|
||||
get => (object)GetValue(TitleContentProperty);
|
||||
get => (object?)GetValue(TitleContentProperty);
|
||||
set => SetValue(TitleContentProperty, value);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
|
||||
set => SetValue(ContentProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty DividerVisibilityProperty = DependencyProperty.Register(nameof(DividerVisibility), typeof(Visibility), typeof(Card), new PropertyMetadata(defaultValue: null));
|
||||
public static readonly DependencyProperty DividerVisibilityProperty = DependencyProperty.Register(nameof(DividerVisibility), typeof(Visibility), typeof(Card), new PropertyMetadata(defaultValue: Visibility.Visible));
|
||||
|
||||
public Visibility DividerVisibility
|
||||
{
|
||||
@@ -66,7 +66,6 @@ namespace Microsoft.PowerToys.Settings.UI.Controls
|
||||
else
|
||||
{
|
||||
VisualStateManager.GoToState(this, "TitleGridVisible", true);
|
||||
DividerVisibility = Visibility.Visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// 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 ManagedCommon;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Controls
|
||||
{
|
||||
public interface IQuickAccessLauncher
|
||||
{
|
||||
bool Launch(ModuleType moduleType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// 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.Windows.Input;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
||||
using Microsoft.UI.Xaml;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Controls
|
||||
{
|
||||
public sealed class QuickAccessItem : Observable
|
||||
{
|
||||
private string _title = string.Empty;
|
||||
|
||||
public string Title
|
||||
{
|
||||
get => _title;
|
||||
set => Set(ref _title, value);
|
||||
}
|
||||
|
||||
private string _description = string.Empty;
|
||||
|
||||
public string Description
|
||||
{
|
||||
get => _description;
|
||||
set => Set(ref _description, value);
|
||||
}
|
||||
|
||||
private string _icon = string.Empty;
|
||||
|
||||
public string Icon
|
||||
{
|
||||
get => _icon;
|
||||
set => Set(ref _icon, value);
|
||||
}
|
||||
|
||||
private ICommand? _command;
|
||||
|
||||
public ICommand? Command
|
||||
{
|
||||
get => _command;
|
||||
set => Set(ref _command, value);
|
||||
}
|
||||
|
||||
private object? _commandParameter;
|
||||
|
||||
public object? CommandParameter
|
||||
{
|
||||
get => _commandParameter;
|
||||
set => Set(ref _commandParameter, value);
|
||||
}
|
||||
|
||||
private bool _visible = true;
|
||||
|
||||
public bool Visible
|
||||
{
|
||||
get => _visible;
|
||||
set => Set(ref _visible, value);
|
||||
}
|
||||
|
||||
private object? _tag;
|
||||
|
||||
public object? Tag
|
||||
{
|
||||
get => _tag;
|
||||
set => Set(ref _tag, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
// 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.Threading;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using PowerToys.Interop;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Controls
|
||||
{
|
||||
public class QuickAccessLauncher : IQuickAccessLauncher
|
||||
{
|
||||
private readonly bool _isElevated;
|
||||
|
||||
public QuickAccessLauncher(bool isElevated)
|
||||
{
|
||||
_isElevated = isElevated;
|
||||
}
|
||||
|
||||
public virtual bool Launch(ModuleType moduleType)
|
||||
{
|
||||
switch (moduleType)
|
||||
{
|
||||
case ModuleType.ColorPicker:
|
||||
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShowColorPickerSharedEvent()))
|
||||
{
|
||||
eventHandle.Set();
|
||||
}
|
||||
|
||||
return true;
|
||||
case ModuleType.EnvironmentVariables:
|
||||
{
|
||||
bool launchAdmin = SettingsRepository<EnvironmentVariablesSettings>.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.LaunchAdministrator;
|
||||
string eventName = !_isElevated && launchAdmin
|
||||
? Constants.ShowEnvironmentVariablesAdminSharedEvent()
|
||||
: Constants.ShowEnvironmentVariablesSharedEvent();
|
||||
|
||||
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName))
|
||||
{
|
||||
eventHandle.Set();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
case ModuleType.FancyZones:
|
||||
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.FZEToggleEvent()))
|
||||
{
|
||||
eventHandle.Set();
|
||||
}
|
||||
|
||||
return true;
|
||||
case ModuleType.Hosts:
|
||||
{
|
||||
bool launchAdmin = SettingsRepository<HostsSettings>.GetInstance(SettingsUtils.Default).SettingsConfig.Properties.LaunchAdministrator;
|
||||
string eventName = !_isElevated && launchAdmin
|
||||
? Constants.ShowHostsAdminSharedEvent()
|
||||
: Constants.ShowHostsSharedEvent();
|
||||
|
||||
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName))
|
||||
{
|
||||
eventHandle.Set();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
case ModuleType.PowerLauncher:
|
||||
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.PowerLauncherSharedEvent()))
|
||||
{
|
||||
eventHandle.Set();
|
||||
}
|
||||
|
||||
return true;
|
||||
case ModuleType.PowerOCR:
|
||||
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShowPowerOCRSharedEvent()))
|
||||
{
|
||||
eventHandle.Set();
|
||||
}
|
||||
|
||||
return true;
|
||||
case ModuleType.RegistryPreview:
|
||||
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.RegistryPreviewTriggerEvent()))
|
||||
{
|
||||
eventHandle.Set();
|
||||
}
|
||||
|
||||
return true;
|
||||
case ModuleType.MeasureTool:
|
||||
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.MeasureToolTriggerEvent()))
|
||||
{
|
||||
eventHandle.Set();
|
||||
}
|
||||
|
||||
return true;
|
||||
case ModuleType.ShortcutGuide:
|
||||
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShortcutGuideTriggerEvent()))
|
||||
{
|
||||
eventHandle.Set();
|
||||
}
|
||||
|
||||
return true;
|
||||
case ModuleType.CmdPal:
|
||||
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.ShowCmdPalEvent()))
|
||||
{
|
||||
eventHandle.Set();
|
||||
}
|
||||
|
||||
return true;
|
||||
case ModuleType.Workspaces:
|
||||
using (var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, Constants.WorkspacesLaunchEditorEvent()))
|
||||
{
|
||||
eventHandle.Set();
|
||||
}
|
||||
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<UserControl
|
||||
x:Class="Microsoft.PowerToys.Settings.UI.Controls.QuickAccessList"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:Microsoft.PowerToys.Settings.UI.Controls"
|
||||
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:tkconverters="using:CommunityToolkit.WinUI.Converters">
|
||||
|
||||
<UserControl.Resources>
|
||||
<tkconverters:BoolToVisibilityConverter
|
||||
x:Key="BoolToVisibilityConverter"
|
||||
FalseValue="Collapsed"
|
||||
TrueValue="Visible" />
|
||||
<tkconverters:StringVisibilityConverter x:Key="StringVisibilityConverter" />
|
||||
</UserControl.Resources>
|
||||
|
||||
<ItemsControl ItemsSource="{x:Bind ItemsSource, Mode=OneWay}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<tkcontrols:WrapPanel HorizontalAlignment="Stretch" VerticalSpacing="12" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="local:QuickAccessItem">
|
||||
<local:FlyoutMenuButton
|
||||
AutomationProperties.Name="{x:Bind Title}"
|
||||
Command="{x:Bind Command}"
|
||||
CommandParameter="{x:Bind CommandParameter}"
|
||||
Visibility="{x:Bind Visible, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<local:FlyoutMenuButton.Content>
|
||||
<TextBlock
|
||||
Style="{StaticResource CaptionTextBlockStyle}"
|
||||
Text="{x:Bind Title}"
|
||||
TextAlignment="Center"
|
||||
TextWrapping="Wrap" />
|
||||
</local:FlyoutMenuButton.Content>
|
||||
<local:FlyoutMenuButton.Icon>
|
||||
<Image>
|
||||
<Image.Source>
|
||||
<BitmapImage UriSource="{x:Bind Icon}" />
|
||||
</Image.Source>
|
||||
</Image>
|
||||
</local:FlyoutMenuButton.Icon>
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip Content="{x:Bind Description}" Visibility="{x:Bind Description, Converter={StaticResource StringVisibilityConverter}}" />
|
||||
</ToolTipService.ToolTip>
|
||||
</local:FlyoutMenuButton>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Controls
|
||||
{
|
||||
public sealed partial class QuickAccessList : UserControl
|
||||
{
|
||||
public QuickAccessList()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
public object ItemsSource
|
||||
{
|
||||
get => (object)GetValue(ItemsSourceProperty);
|
||||
set => SetValue(ItemsSourceProperty, value);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(nameof(ItemsSource), typeof(object), typeof(QuickAccessList), new PropertyMetadata(null));
|
||||
}
|
||||
}
|
||||
@@ -1,44 +1,64 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
using global::PowerToys.GPOWrapper;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.ViewModels.Commands;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.Windows.ApplicationModel.Resources;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
namespace Microsoft.PowerToys.Settings.UI.Controls
|
||||
{
|
||||
public partial class LauncherViewModel : Observable
|
||||
public partial class QuickAccessViewModel : Observable
|
||||
{
|
||||
public bool IsUpdateAvailable { get; set; }
|
||||
private readonly ISettingsRepository<GeneralSettings> _settingsRepository;
|
||||
private readonly IQuickAccessLauncher _launcher;
|
||||
private readonly Func<ModuleType, bool> _isModuleGpoDisabled;
|
||||
private readonly ResourceLoader _resourceLoader;
|
||||
private readonly DispatcherQueue _dispatcherQueue;
|
||||
private GeneralSettings _generalSettings;
|
||||
|
||||
public ObservableCollection<FlyoutMenuItem> FlyoutMenuItems { get; set; }
|
||||
public ObservableCollection<QuickAccessItem> Items { get; } = new();
|
||||
|
||||
private GeneralSettings generalSettingsConfig;
|
||||
private UpdatingSettings updatingSettingsConfig;
|
||||
private ISettingsRepository<GeneralSettings> _settingsRepository;
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
private Func<string, int> SendIPCMessage { get; }
|
||||
|
||||
public LauncherViewModel(ISettingsRepository<GeneralSettings> settingsRepository, Func<string, int> ipcMSGCallBackFunc)
|
||||
public QuickAccessViewModel(
|
||||
ISettingsRepository<GeneralSettings> settingsRepository,
|
||||
IQuickAccessLauncher launcher,
|
||||
Func<ModuleType, bool> isModuleGpoDisabled,
|
||||
ResourceLoader resourceLoader)
|
||||
{
|
||||
_settingsRepository = settingsRepository;
|
||||
generalSettingsConfig = settingsRepository.SettingsConfig;
|
||||
generalSettingsConfig.AddEnabledModuleChangeNotification(ModuleEnabledChanged);
|
||||
_launcher = launcher;
|
||||
_isModuleGpoDisabled = isModuleGpoDisabled;
|
||||
_resourceLoader = resourceLoader;
|
||||
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
|
||||
|
||||
// set the callback functions value to handle outgoing IPC message.
|
||||
SendIPCMessage = ipcMSGCallBackFunc;
|
||||
resourceLoader = ResourceLoaderInstance.ResourceLoader;
|
||||
FlyoutMenuItems = new ObservableCollection<FlyoutMenuItem>();
|
||||
_generalSettings = _settingsRepository.SettingsConfig;
|
||||
_generalSettings.AddEnabledModuleChangeNotification(ModuleEnabledChanged);
|
||||
_settingsRepository.SettingsChanged += OnSettingsChanged;
|
||||
|
||||
InitializeItems();
|
||||
}
|
||||
|
||||
private void OnSettingsChanged(GeneralSettings newSettings)
|
||||
{
|
||||
if (_dispatcherQueue != null)
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
_generalSettings = newSettings;
|
||||
_generalSettings.AddEnabledModuleChangeNotification(ModuleEnabledChanged);
|
||||
RefreshItemsVisibility();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeItems()
|
||||
{
|
||||
AddFlyoutMenuItem(ModuleType.ColorPicker);
|
||||
AddFlyoutMenuItem(ModuleType.CmdPal);
|
||||
AddFlyoutMenuItem(ModuleType.EnvironmentVariables);
|
||||
@@ -50,40 +70,50 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
AddFlyoutMenuItem(ModuleType.MeasureTool);
|
||||
AddFlyoutMenuItem(ModuleType.ShortcutGuide);
|
||||
AddFlyoutMenuItem(ModuleType.Workspaces);
|
||||
|
||||
updatingSettingsConfig = UpdatingSettings.LoadSettings();
|
||||
if (updatingSettingsConfig == null)
|
||||
{
|
||||
updatingSettingsConfig = new UpdatingSettings();
|
||||
}
|
||||
|
||||
if (updatingSettingsConfig.State == UpdatingSettings.UpdatingState.ReadyToInstall || updatingSettingsConfig.State == UpdatingSettings.UpdatingState.ReadyToDownload)
|
||||
{
|
||||
IsUpdateAvailable = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
IsUpdateAvailable = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddFlyoutMenuItem(ModuleType moduleType)
|
||||
{
|
||||
if (ModuleHelper.GetModuleGpoConfiguration(moduleType) == GpoRuleConfigured.Disabled)
|
||||
if (_isModuleGpoDisabled(moduleType))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FlyoutMenuItems.Add(new FlyoutMenuItem()
|
||||
Items.Add(new QuickAccessItem
|
||||
{
|
||||
Label = resourceLoader.GetString(ModuleHelper.GetModuleLabelResourceName(moduleType)),
|
||||
Title = _resourceLoader.GetString(Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetModuleLabelResourceName(moduleType)),
|
||||
Tag = moduleType,
|
||||
Visible = ModuleHelper.GetIsModuleEnabled(generalSettingsConfig, moduleType),
|
||||
ToolTip = GetModuleToolTip(moduleType),
|
||||
Icon = ModuleHelper.GetModuleTypeFluentIconName(moduleType),
|
||||
Visible = Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetIsModuleEnabled(_generalSettings, moduleType),
|
||||
Description = GetModuleToolTip(moduleType),
|
||||
Icon = Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetModuleTypeFluentIconName(moduleType),
|
||||
Command = new RelayCommand(() => _launcher.Launch(moduleType)),
|
||||
});
|
||||
}
|
||||
|
||||
private void ModuleEnabledChanged()
|
||||
{
|
||||
if (_dispatcherQueue != null)
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(() =>
|
||||
{
|
||||
_generalSettings = _settingsRepository.SettingsConfig;
|
||||
_generalSettings.AddEnabledModuleChangeNotification(ModuleEnabledChanged);
|
||||
RefreshItemsVisibility();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshItemsVisibility()
|
||||
{
|
||||
foreach (var item in Items)
|
||||
{
|
||||
if (item.Tag is ModuleType moduleType)
|
||||
{
|
||||
item.Visible = Microsoft.PowerToys.Settings.UI.Library.Helpers.ModuleHelper.GetIsModuleEnabled(_generalSettings, moduleType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string GetModuleToolTip(ModuleType moduleType)
|
||||
{
|
||||
return moduleType switch
|
||||
@@ -99,16 +129,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
};
|
||||
}
|
||||
|
||||
private void ModuleEnabledChanged()
|
||||
{
|
||||
generalSettingsConfig = _settingsRepository.SettingsConfig;
|
||||
generalSettingsConfig.AddEnabledModuleChangeNotification(ModuleEnabledChanged);
|
||||
foreach (FlyoutMenuItem item in FlyoutMenuItems)
|
||||
{
|
||||
item.Visible = ModuleHelper.GetIsModuleEnabled(generalSettingsConfig, item.Tag);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetShortcutGuideToolTip()
|
||||
{
|
||||
var shortcutGuideSettings = SettingsRepository<ShortcutGuideSettings>.GetInstance(SettingsUtils.Default).SettingsConfig;
|
||||
@@ -116,15 +136,5 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
? "Win"
|
||||
: shortcutGuideSettings.Properties.OpenShortcutGuide.ToString();
|
||||
}
|
||||
|
||||
internal void StartBugReport()
|
||||
{
|
||||
SendIPCMessage("{\"bugreport\": 0 }");
|
||||
}
|
||||
|
||||
internal void KillRunner()
|
||||
{
|
||||
SendIPCMessage("{\"killrunner\": 0 }");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\..\Common.Dotnet.CsWinRT.props" />
|
||||
<Import Project="..\..\Common.SelfContained.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net9.0-windows10.0.26100.0</TargetFramework>
|
||||
<RootNamespace>Microsoft.PowerToys.Settings.UI.Controls</RootNamespace>
|
||||
<AssemblyName>PowerToys.Settings.UI.Controls</AssemblyName>
|
||||
<UseWinUI>true</UseWinUI>
|
||||
<EnablePreviewMsixTooling>true</EnablePreviewMsixTooling>
|
||||
<GenerateLibraryLayout>true</GenerateLibraryLayout>
|
||||
<ProjectPriFileName>PowerToys.Settings.UI.Controls.pri</ProjectPriFileName>
|
||||
<Nullable>enable</Nullable>
|
||||
<Platforms>x64;ARM64</Platforms>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.SettingsControls" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Extensions" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Converters" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" />
|
||||
<PackageReference Include="WinUIEx" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,138 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Dashboard_SortBy_ToolTip.Text" xml:space="preserve">
|
||||
<value>Sort utilities</value>
|
||||
</data>
|
||||
<data name="Dashboard_SortAlphabetical.Text" xml:space="preserve">
|
||||
<value>Alphabetically</value>
|
||||
</data>
|
||||
<data name="Dashboard_SortByStatus.Text" xml:space="preserve">
|
||||
<value>By status</value>
|
||||
</data>
|
||||
<data name="Dashboard_SortBy.[using:Microsoft.UI.Xaml.Automation]AutomationProperties.Name" xml:space="preserve">
|
||||
<value>Sort utilities</value>
|
||||
</data>
|
||||
<data name="NewInfoBadge.Text" xml:space="preserve">
|
||||
<value>NEW</value>
|
||||
</data>
|
||||
<data name="GPOWarning.Text" xml:space="preserve">
|
||||
<value>This setting is managed by your organization</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1,14 +1,9 @@
|
||||
<!-- Copyright (c) Microsoft Corporation and Contributors. -->
|
||||
<!-- Licensed under the MIT License. -->
|
||||
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls">
|
||||
|
||||
<Style BasedOn="{StaticResource DefaultFlyoutMenuButtonStyle}" TargetType="controls:FlyoutMenuButton" />
|
||||
|
||||
<Style x:Key="DefaultFlyoutMenuButtonStyle" TargetType="controls:FlyoutMenuButton">
|
||||
<Style TargetType="controls:FlyoutMenuButton">
|
||||
<Setter Property="HorizontalAlignment" Value="Stretch" />
|
||||
<Setter Property="VerticalAlignment" Value="Stretch" />
|
||||
<Setter Property="Width" Value="116" />
|
||||
@@ -11,11 +11,13 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
public static readonly HotkeySettings DefaultReparentHotkeyValue = new HotkeySettings(true, true, false, true, 0x52); // Ctrl+Win+Shift+R
|
||||
public static readonly HotkeySettings DefaultThumbnailHotkeyValue = new HotkeySettings(true, true, false, true, 0x54); // Ctrl+Win+Shift+T
|
||||
public static readonly HotkeySettings DefaultScreenshotHotkeyValue = new HotkeySettings(true, true, false, true, 0x53); // Ctrl+Win+Shift+S
|
||||
|
||||
public CropAndLockProperties()
|
||||
{
|
||||
ReparentHotkey = new KeyboardKeysProperty(DefaultReparentHotkeyValue);
|
||||
ThumbnailHotkey = new KeyboardKeysProperty(DefaultThumbnailHotkeyValue);
|
||||
ScreenshotHotkey = new KeyboardKeysProperty(DefaultScreenshotHotkeyValue);
|
||||
}
|
||||
|
||||
[JsonPropertyName("reparent-hotkey")]
|
||||
@@ -23,5 +25,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
|
||||
[JsonPropertyName("thumbnail-hotkey")]
|
||||
public KeyboardKeysProperty ThumbnailHotkey { get; set; }
|
||||
|
||||
[JsonPropertyName("screenshot-hotkey")]
|
||||
public KeyboardKeysProperty ScreenshotHotkey { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
() => Properties.ThumbnailHotkey.Value,
|
||||
value => Properties.ThumbnailHotkey.Value = value ?? CropAndLockProperties.DefaultThumbnailHotkeyValue,
|
||||
"CropAndLock_ThumbnailActivation_Shortcut"),
|
||||
new HotkeyAccessor(
|
||||
() => Properties.ScreenshotHotkey.Value,
|
||||
value => Properties.ScreenshotHotkey.Value = value ?? CropAndLockProperties.DefaultScreenshotHotkeyValue,
|
||||
"CropAndLock_ScreenshotActivation_Shortcut"),
|
||||
};
|
||||
|
||||
return hotkeyAccessors.ToArray();
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
|
||||
using Settings.UI.Library.Attributes;
|
||||
@@ -19,7 +20,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
ByStatus,
|
||||
}
|
||||
|
||||
public class GeneralSettings : ISettingsConfig
|
||||
public class GeneralSettings : ISettingsConfig, IHotkeyConfig
|
||||
{
|
||||
// Gets or sets a value indicating whether run powertoys on start-up.
|
||||
[JsonPropertyName("startup")]
|
||||
@@ -48,6 +49,14 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
[JsonPropertyName("enable_warnings_elevated_apps")]
|
||||
public bool EnableWarningsElevatedApps { get; set; }
|
||||
|
||||
// Gets or sets a value indicating whether Quick Access is enabled.
|
||||
[JsonPropertyName("enable_quick_access")]
|
||||
public bool EnableQuickAccess { get; set; }
|
||||
|
||||
// Gets or sets Quick Access shortcut.
|
||||
[JsonPropertyName("quick_access_shortcut")]
|
||||
public HotkeySettings QuickAccessShortcut { get; set; }
|
||||
|
||||
// Gets or sets theme Name.
|
||||
[JsonPropertyName("theme")]
|
||||
public string Theme { get; set; }
|
||||
@@ -94,6 +103,8 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
ShowSysTrayIcon = true;
|
||||
IsAdmin = false;
|
||||
EnableWarningsElevatedApps = true;
|
||||
EnableQuickAccess = true;
|
||||
QuickAccessShortcut = new HotkeySettings();
|
||||
IsElevated = false;
|
||||
ShowNewUpdatesToastNotification = true;
|
||||
AutoDownloadUpdates = false;
|
||||
@@ -116,6 +127,22 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
IgnoredConflictProperties = new ShortcutConflictProperties();
|
||||
}
|
||||
|
||||
public HotkeyAccessor[] GetAllHotkeyAccessors()
|
||||
{
|
||||
return new HotkeyAccessor[]
|
||||
{
|
||||
new HotkeyAccessor(
|
||||
() => QuickAccessShortcut,
|
||||
(hotkey) => { QuickAccessShortcut = hotkey; },
|
||||
"GeneralPage_QuickAccessShortcut"),
|
||||
};
|
||||
}
|
||||
|
||||
public ModuleType GetModuleType()
|
||||
{
|
||||
return ModuleType.GeneralSettings;
|
||||
}
|
||||
|
||||
// converts the current to a json string.
|
||||
public string ToJsonString()
|
||||
{
|
||||
|
||||
121
src/settings-ui/Settings.UI.Library/Helpers/ModuleHelper.cs
Normal file
121
src/settings-ui/Settings.UI.Library/Helpers/ModuleHelper.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
// 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 ManagedCommon;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library.Helpers
|
||||
{
|
||||
public static class ModuleHelper
|
||||
{
|
||||
public static string GetModuleLabelResourceName(ModuleType moduleType)
|
||||
{
|
||||
return moduleType switch
|
||||
{
|
||||
ModuleType.Workspaces => "Workspaces/ModuleTitle",
|
||||
ModuleType.PowerAccent => "QuickAccent/ModuleTitle",
|
||||
ModuleType.PowerOCR => "TextExtractor/ModuleTitle",
|
||||
ModuleType.FindMyMouse => "MouseUtils_FindMyMouse/Header",
|
||||
ModuleType.MouseHighlighter => "MouseUtils_MouseHighlighter/Header",
|
||||
ModuleType.MouseJump => "MouseUtils_MouseJump/Header",
|
||||
ModuleType.MousePointerCrosshairs => "MouseUtils_MousePointerCrosshairs/Header",
|
||||
ModuleType.CursorWrap => "MouseUtils_CursorWrap/Header",
|
||||
ModuleType.GeneralSettings => "QuickAccessTitle/Title",
|
||||
_ => $"{moduleType}/ModuleTitle",
|
||||
};
|
||||
}
|
||||
|
||||
public static string GetModuleTypeFluentIconName(ModuleType moduleType)
|
||||
{
|
||||
return moduleType switch
|
||||
{
|
||||
ModuleType.AdvancedPaste => "ms-appx:///Assets/Settings/Icons/AdvancedPaste.png",
|
||||
ModuleType.Workspaces => "ms-appx:///Assets/Settings/Icons/Workspaces.png",
|
||||
ModuleType.PowerOCR => "ms-appx:///Assets/Settings/Icons/TextExtractor.png",
|
||||
ModuleType.PowerAccent => "ms-appx:///Assets/Settings/Icons/QuickAccent.png",
|
||||
ModuleType.MousePointerCrosshairs => "ms-appx:///Assets/Settings/Icons/MouseCrosshairs.png",
|
||||
ModuleType.MeasureTool => "ms-appx:///Assets/Settings/Icons/ScreenRuler.png",
|
||||
ModuleType.PowerLauncher => "ms-appx:///Assets/Settings/Icons/PowerToysRun.png",
|
||||
ModuleType.GeneralSettings => "ms-appx:///Assets/Settings/Icons/PowerToys.png",
|
||||
_ => $"ms-appx:///Assets/Settings/Icons/{moduleType}.png",
|
||||
};
|
||||
}
|
||||
|
||||
public static bool GetIsModuleEnabled(GeneralSettings generalSettingsConfig, ModuleType moduleType)
|
||||
{
|
||||
return moduleType switch
|
||||
{
|
||||
ModuleType.AdvancedPaste => generalSettingsConfig.Enabled.AdvancedPaste,
|
||||
ModuleType.AlwaysOnTop => generalSettingsConfig.Enabled.AlwaysOnTop,
|
||||
ModuleType.Awake => generalSettingsConfig.Enabled.Awake,
|
||||
ModuleType.CmdPal => generalSettingsConfig.Enabled.CmdPal,
|
||||
ModuleType.ColorPicker => generalSettingsConfig.Enabled.ColorPicker,
|
||||
ModuleType.CropAndLock => generalSettingsConfig.Enabled.CropAndLock,
|
||||
ModuleType.CursorWrap => generalSettingsConfig.Enabled.CursorWrap,
|
||||
ModuleType.EnvironmentVariables => generalSettingsConfig.Enabled.EnvironmentVariables,
|
||||
ModuleType.FancyZones => generalSettingsConfig.Enabled.FancyZones,
|
||||
ModuleType.FileLocksmith => generalSettingsConfig.Enabled.FileLocksmith,
|
||||
ModuleType.FindMyMouse => generalSettingsConfig.Enabled.FindMyMouse,
|
||||
ModuleType.Hosts => generalSettingsConfig.Enabled.Hosts,
|
||||
ModuleType.ImageResizer => generalSettingsConfig.Enabled.ImageResizer,
|
||||
ModuleType.KeyboardManager => generalSettingsConfig.Enabled.KeyboardManager,
|
||||
ModuleType.LightSwitch => generalSettingsConfig.Enabled.LightSwitch,
|
||||
ModuleType.MouseHighlighter => generalSettingsConfig.Enabled.MouseHighlighter,
|
||||
ModuleType.MouseJump => generalSettingsConfig.Enabled.MouseJump,
|
||||
ModuleType.MousePointerCrosshairs => generalSettingsConfig.Enabled.MousePointerCrosshairs,
|
||||
ModuleType.MouseWithoutBorders => generalSettingsConfig.Enabled.MouseWithoutBorders,
|
||||
ModuleType.NewPlus => generalSettingsConfig.Enabled.NewPlus,
|
||||
ModuleType.Peek => generalSettingsConfig.Enabled.Peek,
|
||||
ModuleType.PowerRename => generalSettingsConfig.Enabled.PowerRename,
|
||||
ModuleType.PowerLauncher => generalSettingsConfig.Enabled.PowerLauncher,
|
||||
ModuleType.PowerAccent => generalSettingsConfig.Enabled.PowerAccent,
|
||||
ModuleType.RegistryPreview => generalSettingsConfig.Enabled.RegistryPreview,
|
||||
ModuleType.MeasureTool => generalSettingsConfig.Enabled.MeasureTool,
|
||||
ModuleType.ShortcutGuide => generalSettingsConfig.Enabled.ShortcutGuide,
|
||||
ModuleType.PowerOCR => generalSettingsConfig.Enabled.PowerOcr,
|
||||
ModuleType.Workspaces => generalSettingsConfig.Enabled.Workspaces,
|
||||
ModuleType.ZoomIt => generalSettingsConfig.Enabled.ZoomIt,
|
||||
ModuleType.GeneralSettings => generalSettingsConfig.EnableQuickAccess,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
public static void SetIsModuleEnabled(GeneralSettings generalSettingsConfig, ModuleType moduleType, bool isEnabled)
|
||||
{
|
||||
switch (moduleType)
|
||||
{
|
||||
case ModuleType.AdvancedPaste: generalSettingsConfig.Enabled.AdvancedPaste = isEnabled; break;
|
||||
case ModuleType.AlwaysOnTop: generalSettingsConfig.Enabled.AlwaysOnTop = isEnabled; break;
|
||||
case ModuleType.Awake: generalSettingsConfig.Enabled.Awake = isEnabled; break;
|
||||
case ModuleType.CmdPal: generalSettingsConfig.Enabled.CmdPal = isEnabled; break;
|
||||
case ModuleType.ColorPicker: generalSettingsConfig.Enabled.ColorPicker = isEnabled; break;
|
||||
case ModuleType.CropAndLock: generalSettingsConfig.Enabled.CropAndLock = isEnabled; break;
|
||||
case ModuleType.CursorWrap: generalSettingsConfig.Enabled.CursorWrap = isEnabled; break;
|
||||
case ModuleType.EnvironmentVariables: generalSettingsConfig.Enabled.EnvironmentVariables = isEnabled; break;
|
||||
case ModuleType.FancyZones: generalSettingsConfig.Enabled.FancyZones = isEnabled; break;
|
||||
case ModuleType.FileLocksmith: generalSettingsConfig.Enabled.FileLocksmith = isEnabled; break;
|
||||
case ModuleType.FindMyMouse: generalSettingsConfig.Enabled.FindMyMouse = isEnabled; break;
|
||||
case ModuleType.Hosts: generalSettingsConfig.Enabled.Hosts = isEnabled; break;
|
||||
case ModuleType.ImageResizer: generalSettingsConfig.Enabled.ImageResizer = isEnabled; break;
|
||||
case ModuleType.KeyboardManager: generalSettingsConfig.Enabled.KeyboardManager = isEnabled; break;
|
||||
case ModuleType.LightSwitch: generalSettingsConfig.Enabled.LightSwitch = isEnabled; break;
|
||||
case ModuleType.MouseHighlighter: generalSettingsConfig.Enabled.MouseHighlighter = isEnabled; break;
|
||||
case ModuleType.MouseJump: generalSettingsConfig.Enabled.MouseJump = isEnabled; break;
|
||||
case ModuleType.MousePointerCrosshairs: generalSettingsConfig.Enabled.MousePointerCrosshairs = isEnabled; break;
|
||||
case ModuleType.MouseWithoutBorders: generalSettingsConfig.Enabled.MouseWithoutBorders = isEnabled; break;
|
||||
case ModuleType.NewPlus: generalSettingsConfig.Enabled.NewPlus = isEnabled; break;
|
||||
case ModuleType.Peek: generalSettingsConfig.Enabled.Peek = isEnabled; break;
|
||||
case ModuleType.PowerRename: generalSettingsConfig.Enabled.PowerRename = isEnabled; break;
|
||||
case ModuleType.PowerLauncher: generalSettingsConfig.Enabled.PowerLauncher = isEnabled; break;
|
||||
case ModuleType.PowerAccent: generalSettingsConfig.Enabled.PowerAccent = isEnabled; break;
|
||||
case ModuleType.RegistryPreview: generalSettingsConfig.Enabled.RegistryPreview = isEnabled; break;
|
||||
case ModuleType.MeasureTool: generalSettingsConfig.Enabled.MeasureTool = isEnabled; break;
|
||||
case ModuleType.ShortcutGuide: generalSettingsConfig.Enabled.ShortcutGuide = isEnabled; break;
|
||||
case ModuleType.PowerOCR: generalSettingsConfig.Enabled.PowerOcr = isEnabled; break;
|
||||
case ModuleType.Workspaces: generalSettingsConfig.Enabled.Workspaces = isEnabled; break;
|
||||
case ModuleType.ZoomIt: generalSettingsConfig.Enabled.ZoomIt = isEnabled; break;
|
||||
case ModuleType.GeneralSettings: generalSettingsConfig.EnableQuickAccess = isEnabled; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library.Interfaces
|
||||
{
|
||||
public interface ISettingsRepository<T>
|
||||
@@ -9,5 +11,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library.Interfaces
|
||||
T SettingsConfig { get; set; }
|
||||
|
||||
bool ReloadSettings();
|
||||
|
||||
event Action<T> SettingsChanged;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,11 @@ namespace Microsoft.PowerToys.Settings.UI.Services
|
||||
if (settingsInstance != null)
|
||||
{
|
||||
var moduleName = settingsInstance.GetModuleName();
|
||||
if (string.IsNullOrEmpty(moduleName) && type == typeof(GeneralSettings))
|
||||
{
|
||||
moduleName = "GeneralSettings";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(moduleName))
|
||||
{
|
||||
settingsTypes[moduleName] = type;
|
||||
@@ -104,7 +109,13 @@ namespace Microsoft.PowerToys.Settings.UI.Services
|
||||
var genericMethod = getSettingsMethod?.MakeGenericMethod(settingsType);
|
||||
|
||||
// Call GetSettingsOrDefault<T>(moduleKey) to get fresh settings from file
|
||||
var freshSettings = genericMethod?.Invoke(_settingsUtils, new object[] { moduleKey, "settings.json" });
|
||||
string actualModuleKey = moduleKey;
|
||||
if (moduleKey == "GeneralSettings")
|
||||
{
|
||||
actualModuleKey = string.Empty;
|
||||
}
|
||||
|
||||
var freshSettings = genericMethod?.Invoke(_settingsUtils, new object[] { actualModuleKey, "settings.json" });
|
||||
|
||||
return freshSettings as IHotkeyConfig;
|
||||
}
|
||||
|
||||
@@ -3,15 +3,16 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
// This Singleton class is a wrapper around the settings configurations that are accessed by viewmodels.
|
||||
// This class can have only one instance and therefore the settings configurations are common to all.
|
||||
public class SettingsRepository<T> : ISettingsRepository<T>
|
||||
public sealed class SettingsRepository<T> : ISettingsRepository<T>, IDisposable
|
||||
where T : class, ISettingsConfig, new()
|
||||
{
|
||||
private static readonly Lock _SettingsRepoLock = new Lock();
|
||||
@@ -22,6 +23,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
|
||||
private T settingsConfig;
|
||||
|
||||
private FileSystemWatcher _watcher;
|
||||
|
||||
public event Action<T> SettingsChanged;
|
||||
|
||||
// Suppressing the warning as this is a singleton class and this method is
|
||||
// necessarily static
|
||||
#pragma warning disable CA1000 // Do not declare static members on generic types
|
||||
@@ -35,6 +40,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
settingsRepository = new SettingsRepository<T>();
|
||||
_settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils));
|
||||
settingsRepository.InitializeWatcher();
|
||||
}
|
||||
|
||||
return settingsRepository;
|
||||
@@ -46,12 +52,51 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
}
|
||||
|
||||
private void InitializeWatcher()
|
||||
{
|
||||
try
|
||||
{
|
||||
var settingsItem = new T();
|
||||
var filePath = _settingsUtils.GetSettingsFilePath(settingsItem.GetModuleName());
|
||||
var directory = Path.GetDirectoryName(filePath);
|
||||
var fileName = Path.GetFileName(filePath);
|
||||
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
_watcher = new FileSystemWatcher(directory, fileName);
|
||||
_watcher.NotifyFilter = NotifyFilters.LastWrite;
|
||||
_watcher.Changed += Watcher_Changed;
|
||||
_watcher.EnableRaisingEvents = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Failed to initialize settings watcher for {typeof(T).Name}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void Watcher_Changed(object sender, FileSystemEventArgs e)
|
||||
{
|
||||
// Wait a bit for the file write to complete and retry if needed
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
Thread.Sleep(100);
|
||||
if (ReloadSettings())
|
||||
{
|
||||
SettingsChanged?.Invoke(SettingsConfig);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ReloadSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
T settingsItem = new T();
|
||||
settingsConfig = _settingsUtils.GetSettingsOrDefault<T>(settingsItem.GetModuleName());
|
||||
settingsConfig = _settingsUtils.GetSettings<T>(settingsItem.GetModuleName());
|
||||
|
||||
SettingsConfig = settingsConfig;
|
||||
|
||||
@@ -85,5 +130,26 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void StopWatching()
|
||||
{
|
||||
if (_watcher != null)
|
||||
{
|
||||
_watcher.EnableRaisingEvents = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void StartWatching()
|
||||
{
|
||||
if (_watcher != null)
|
||||
{
|
||||
_watcher.EnableRaisingEvents = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_watcher?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
|
||||
// Main Settings Classes
|
||||
[JsonSerializable(typeof(GeneralSettings))]
|
||||
[JsonSerializable(typeof(OutGoingGeneralSettings))]
|
||||
[JsonSerializable(typeof(AdvancedPasteSettings))]
|
||||
[JsonSerializable(typeof(AlwaysOnTopSettings))]
|
||||
[JsonSerializable(typeof(AwakeSettings))]
|
||||
|
||||
@@ -30,6 +30,11 @@ namespace Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility
|
||||
private readonly SettingsUtils _settingsUtils;
|
||||
private T _settingsConfig;
|
||||
|
||||
// Implements ISettingsRepository<T>.SettingsChanged
|
||||
#pragma warning disable CS0067
|
||||
public event System.Action<T> SettingsChanged;
|
||||
#pragma warning restore CS0067
|
||||
|
||||
public MockSettingsRepository(SettingsUtils settingsUtils)
|
||||
{
|
||||
_settingsUtils = settingsUtils;
|
||||
|
||||
@@ -28,6 +28,28 @@ namespace ViewModelTests
|
||||
mockGeneralSettingsUtils = ISettingsUtilsMocks.GetStubSettingsUtils<GeneralSettings>();
|
||||
}
|
||||
|
||||
private sealed class TestGeneralViewModel : GeneralViewModel
|
||||
{
|
||||
public TestGeneralViewModel(
|
||||
Microsoft.PowerToys.Settings.UI.Library.Interfaces.ISettingsRepository<GeneralSettings> settingsRepository,
|
||||
string runAsAdminText,
|
||||
string runAsUserText,
|
||||
bool isElevated,
|
||||
bool isAdmin,
|
||||
Func<string, int> ipcMSGCallBackFunc,
|
||||
Func<string, int> ipcMSGRestartAsAdminMSGCallBackFunc,
|
||||
Func<string, int> ipcMSGCheckForUpdatesCallBackFunc,
|
||||
string configFileSubfolder = "")
|
||||
: base(settingsRepository, runAsAdminText, runAsUserText, isElevated, isAdmin, ipcMSGCallBackFunc, ipcMSGRestartAsAdminMSGCallBackFunc, ipcMSGCheckForUpdatesCallBackFunc, configFileSubfolder)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Microsoft.UI.Dispatching.DispatcherQueue GetDispatcherQueue()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DataRow("v0.18.2")]
|
||||
[DataRow("v0.19.2")]
|
||||
@@ -49,7 +71,7 @@ namespace ViewModelTests
|
||||
Func<string, int> sendMockIPCConfigMSG = msg => 0;
|
||||
Func<string, int> sendRestartAdminIPCMessage = msg => 0;
|
||||
Func<string, int> sendCheckForUpdatesIPCMessage = msg => 0;
|
||||
var viewModel = new GeneralViewModel(
|
||||
var viewModel = new TestGeneralViewModel(
|
||||
settingsRepository: generalSettingsRepository,
|
||||
runAsAdminText: "GeneralSettings_RunningAsAdminText",
|
||||
runAsUserText: "GeneralSettings_RunningAsUserText",
|
||||
@@ -78,7 +100,7 @@ namespace ViewModelTests
|
||||
Func<string, int> sendMockIPCConfigMSG = msg => { return 0; };
|
||||
Func<string, int> sendRestartAdminIPCMessage = msg => { return 0; };
|
||||
Func<string, int> sendCheckForUpdatesIPCMessage = msg => { return 0; };
|
||||
GeneralViewModel viewModel = new GeneralViewModel(
|
||||
GeneralViewModel viewModel = new TestGeneralViewModel(
|
||||
settingsRepository: SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object),
|
||||
"GeneralSettings_RunningAsAdminText",
|
||||
"GeneralSettings_RunningAsUserText",
|
||||
@@ -114,7 +136,7 @@ namespace ViewModelTests
|
||||
// Arrange
|
||||
Func<string, int> sendRestartAdminIPCMessage = msg => { return 0; };
|
||||
Func<string, int> sendCheckForUpdatesIPCMessage = msg => { return 0; };
|
||||
GeneralViewModel viewModel = new GeneralViewModel(
|
||||
GeneralViewModel viewModel = new TestGeneralViewModel(
|
||||
settingsRepository: SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object),
|
||||
"GeneralSettings_RunningAsAdminText",
|
||||
"GeneralSettings_RunningAsUserText",
|
||||
@@ -145,7 +167,7 @@ namespace ViewModelTests
|
||||
Func<string, int> sendCheckForUpdatesIPCMessage = msg => { return 0; };
|
||||
|
||||
// Arrange
|
||||
GeneralViewModel viewModel = new GeneralViewModel(
|
||||
GeneralViewModel viewModel = new TestGeneralViewModel(
|
||||
settingsRepository: SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object),
|
||||
"GeneralSettings_RunningAsAdminText",
|
||||
"GeneralSettings_RunningAsUserText",
|
||||
@@ -178,7 +200,7 @@ namespace ViewModelTests
|
||||
|
||||
Func<string, int> sendRestartAdminIPCMessage = msg => { return 0; };
|
||||
Func<string, int> sendCheckForUpdatesIPCMessage = msg => { return 0; };
|
||||
viewModel = new GeneralViewModel(
|
||||
viewModel = new TestGeneralViewModel(
|
||||
settingsRepository: SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object),
|
||||
"GeneralSettings_RunningAsAdminText",
|
||||
"GeneralSettings_RunningAsUserText",
|
||||
@@ -208,7 +230,7 @@ namespace ViewModelTests
|
||||
|
||||
Func<string, int> sendRestartAdminIPCMessage = msg => { return 0; };
|
||||
Func<string, int> sendCheckForUpdatesIPCMessage = msg => { return 0; };
|
||||
GeneralViewModel viewModel = new GeneralViewModel(
|
||||
GeneralViewModel viewModel = new TestGeneralViewModel(
|
||||
settingsRepository: SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object),
|
||||
"GeneralSettings_RunningAsAdminText",
|
||||
"GeneralSettings_RunningAsUserText",
|
||||
@@ -238,7 +260,7 @@ namespace ViewModelTests
|
||||
|
||||
Func<string, int> sendRestartAdminIPCMessage = msg => { return 0; };
|
||||
Func<string, int> sendCheckForUpdatesIPCMessage = msg => { return 0; };
|
||||
GeneralViewModel viewModel = new(
|
||||
GeneralViewModel viewModel = new TestGeneralViewModel(
|
||||
settingsRepository: SettingsRepository<GeneralSettings>.GetInstance(mockGeneralSettingsUtils.Object),
|
||||
"GeneralSettings_RunningAsAdminText",
|
||||
"GeneralSettings_RunningAsUserText",
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using Microsoft.PowerToys.Settings.UI.Controls;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Converters
|
||||
{
|
||||
public partial class EnumToModuleListSortOptionConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is DashboardSortOrder sortOrder)
|
||||
{
|
||||
return sortOrder switch
|
||||
{
|
||||
DashboardSortOrder.Alphabetical => ModuleListSortOption.Alphabetical,
|
||||
DashboardSortOrder.ByStatus => ModuleListSortOption.ByStatus,
|
||||
_ => ModuleListSortOption.Alphabetical,
|
||||
};
|
||||
}
|
||||
|
||||
return ModuleListSortOption.Alphabetical;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
if (value is ModuleListSortOption sortOption)
|
||||
{
|
||||
return sortOption switch
|
||||
{
|
||||
ModuleListSortOption.Alphabetical => DashboardSortOrder.Alphabetical,
|
||||
ModuleListSortOption.ByStatus => DashboardSortOrder.ByStatus,
|
||||
_ => DashboardSortOrder.Alphabetical,
|
||||
};
|
||||
}
|
||||
|
||||
return DashboardSortOrder.Alphabetical;
|
||||
}
|
||||
}
|
||||
}
|
||||
91
src/settings-ui/Settings.UI/Helpers/ModuleGpoHelper.cs
Normal file
91
src/settings-ui/Settings.UI/Helpers/ModuleGpoHelper.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
// 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 global::PowerToys.GPOWrapper;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Views;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Helpers
|
||||
{
|
||||
internal sealed class ModuleGpoHelper
|
||||
{
|
||||
public static GpoRuleConfigured GetModuleGpoConfiguration(ModuleType moduleType)
|
||||
{
|
||||
switch (moduleType)
|
||||
{
|
||||
case ModuleType.AdvancedPaste: return GPOWrapper.GetConfiguredAdvancedPasteEnabledValue();
|
||||
case ModuleType.AlwaysOnTop: return GPOWrapper.GetConfiguredAlwaysOnTopEnabledValue();
|
||||
case ModuleType.Awake: return GPOWrapper.GetConfiguredAwakeEnabledValue();
|
||||
case ModuleType.CmdPal: return GPOWrapper.GetConfiguredCmdPalEnabledValue();
|
||||
case ModuleType.ColorPicker: return GPOWrapper.GetConfiguredColorPickerEnabledValue();
|
||||
case ModuleType.CropAndLock: return GPOWrapper.GetConfiguredCropAndLockEnabledValue();
|
||||
case ModuleType.CursorWrap: return GPOWrapper.GetConfiguredCursorWrapEnabledValue();
|
||||
case ModuleType.EnvironmentVariables: return GPOWrapper.GetConfiguredEnvironmentVariablesEnabledValue();
|
||||
case ModuleType.FancyZones: return GPOWrapper.GetConfiguredFancyZonesEnabledValue();
|
||||
case ModuleType.FileLocksmith: return GPOWrapper.GetConfiguredFileLocksmithEnabledValue();
|
||||
case ModuleType.FindMyMouse: return GPOWrapper.GetConfiguredFindMyMouseEnabledValue();
|
||||
case ModuleType.Hosts: return GPOWrapper.GetConfiguredHostsFileEditorEnabledValue();
|
||||
case ModuleType.ImageResizer: return GPOWrapper.GetConfiguredImageResizerEnabledValue();
|
||||
case ModuleType.KeyboardManager: return GPOWrapper.GetConfiguredKeyboardManagerEnabledValue();
|
||||
case ModuleType.MouseHighlighter: return GPOWrapper.GetConfiguredMouseHighlighterEnabledValue();
|
||||
case ModuleType.MouseJump: return GPOWrapper.GetConfiguredMouseJumpEnabledValue();
|
||||
case ModuleType.MousePointerCrosshairs: return GPOWrapper.GetConfiguredMousePointerCrosshairsEnabledValue();
|
||||
case ModuleType.MouseWithoutBorders: return GPOWrapper.GetConfiguredMouseWithoutBordersEnabledValue();
|
||||
case ModuleType.NewPlus: return GPOWrapper.GetConfiguredNewPlusEnabledValue();
|
||||
case ModuleType.Peek: return GPOWrapper.GetConfiguredPeekEnabledValue();
|
||||
case ModuleType.PowerRename: return GPOWrapper.GetConfiguredPowerRenameEnabledValue();
|
||||
case ModuleType.PowerLauncher: return GPOWrapper.GetConfiguredPowerLauncherEnabledValue();
|
||||
case ModuleType.PowerAccent: return GPOWrapper.GetConfiguredQuickAccentEnabledValue();
|
||||
case ModuleType.Workspaces: return GPOWrapper.GetConfiguredWorkspacesEnabledValue();
|
||||
case ModuleType.RegistryPreview: return GPOWrapper.GetConfiguredRegistryPreviewEnabledValue();
|
||||
case ModuleType.MeasureTool: return GPOWrapper.GetConfiguredScreenRulerEnabledValue();
|
||||
case ModuleType.ShortcutGuide: return GPOWrapper.GetConfiguredShortcutGuideEnabledValue();
|
||||
case ModuleType.PowerOCR: return GPOWrapper.GetConfiguredTextExtractorEnabledValue();
|
||||
case ModuleType.ZoomIt: return GPOWrapper.GetConfiguredZoomItEnabledValue();
|
||||
default: return GpoRuleConfigured.Unavailable;
|
||||
}
|
||||
}
|
||||
|
||||
public static System.Type GetModulePageType(ModuleType moduleType)
|
||||
{
|
||||
return moduleType switch
|
||||
{
|
||||
ModuleType.AdvancedPaste => typeof(AdvancedPastePage),
|
||||
ModuleType.AlwaysOnTop => typeof(AlwaysOnTopPage),
|
||||
ModuleType.Awake => typeof(AwakePage),
|
||||
ModuleType.CmdPal => typeof(CmdPalPage),
|
||||
ModuleType.ColorPicker => typeof(ColorPickerPage),
|
||||
ModuleType.CropAndLock => typeof(CropAndLockPage),
|
||||
ModuleType.CursorWrap => typeof(MouseUtilsPage),
|
||||
ModuleType.LightSwitch => typeof(LightSwitchPage),
|
||||
ModuleType.EnvironmentVariables => typeof(EnvironmentVariablesPage),
|
||||
ModuleType.FancyZones => typeof(FancyZonesPage),
|
||||
ModuleType.FileLocksmith => typeof(FileLocksmithPage),
|
||||
ModuleType.FindMyMouse => typeof(MouseUtilsPage),
|
||||
ModuleType.GeneralSettings => typeof(GeneralPage),
|
||||
ModuleType.Hosts => typeof(HostsPage),
|
||||
ModuleType.ImageResizer => typeof(ImageResizerPage),
|
||||
ModuleType.KeyboardManager => typeof(KeyboardManagerPage),
|
||||
ModuleType.MouseHighlighter => typeof(MouseUtilsPage),
|
||||
ModuleType.MouseJump => typeof(MouseUtilsPage),
|
||||
ModuleType.MousePointerCrosshairs => typeof(MouseUtilsPage),
|
||||
ModuleType.MouseWithoutBorders => typeof(MouseWithoutBordersPage),
|
||||
ModuleType.NewPlus => typeof(NewPlusPage),
|
||||
ModuleType.Peek => typeof(PeekPage),
|
||||
ModuleType.PowerRename => typeof(PowerRenamePage),
|
||||
ModuleType.PowerLauncher => typeof(PowerLauncherPage),
|
||||
ModuleType.PowerAccent => typeof(PowerAccentPage),
|
||||
ModuleType.Workspaces => typeof(WorkspacesPage),
|
||||
ModuleType.RegistryPreview => typeof(RegistryPreviewPage),
|
||||
ModuleType.MeasureTool => typeof(MeasureToolPage),
|
||||
ModuleType.ShortcutGuide => typeof(ShortcutGuidePage),
|
||||
ModuleType.PowerOCR => typeof(PowerOcrPage),
|
||||
ModuleType.ZoomIt => typeof(ZoomItPage),
|
||||
_ => typeof(DashboardPage), // never called, all values listed above
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,196 +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 global::PowerToys.GPOWrapper;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Views;
|
||||
using Windows.UI;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Helpers
|
||||
{
|
||||
internal sealed class ModuleHelper
|
||||
{
|
||||
public static string GetModuleLabelResourceName(ModuleType moduleType)
|
||||
{
|
||||
switch (moduleType)
|
||||
{
|
||||
case ModuleType.Workspaces: return "Workspaces/ModuleTitle";
|
||||
case ModuleType.PowerAccent: return "QuickAccent/ModuleTitle";
|
||||
case ModuleType.PowerOCR: return "TextExtractor/ModuleTitle";
|
||||
case ModuleType.FindMyMouse:
|
||||
case ModuleType.MouseHighlighter:
|
||||
case ModuleType.MouseJump:
|
||||
case ModuleType.MousePointerCrosshairs:
|
||||
case ModuleType.CursorWrap: return $"MouseUtils_{moduleType}/Header";
|
||||
default: return $"{moduleType}/ModuleTitle";
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetModuleTypeFluentIconName(ModuleType moduleType)
|
||||
{
|
||||
switch (moduleType)
|
||||
{
|
||||
case ModuleType.AdvancedPaste: return "ms-appx:///Assets/Settings/Icons/AdvancedPaste.png";
|
||||
case ModuleType.Workspaces: return "ms-appx:///Assets/Settings/Icons/Workspaces.png";
|
||||
case ModuleType.PowerOCR: return "ms-appx:///Assets/Settings/Icons/TextExtractor.png";
|
||||
case ModuleType.PowerAccent: return "ms-appx:///Assets/Settings/Icons/QuickAccent.png";
|
||||
case ModuleType.MousePointerCrosshairs: return "ms-appx:///Assets/Settings/Icons/MouseCrosshairs.png";
|
||||
case ModuleType.MeasureTool: return "ms-appx:///Assets/Settings/Icons/ScreenRuler.png";
|
||||
case ModuleType.PowerLauncher: return $"ms-appx:///Assets/Settings/Icons/PowerToysRun.png";
|
||||
default: return $"ms-appx:///Assets/Settings/Icons/{moduleType}.png";
|
||||
}
|
||||
}
|
||||
|
||||
public static bool GetIsModuleEnabled(Library.GeneralSettings generalSettingsConfig, ModuleType moduleType)
|
||||
{
|
||||
switch (moduleType)
|
||||
{
|
||||
case ModuleType.AdvancedPaste: return generalSettingsConfig.Enabled.AdvancedPaste;
|
||||
case ModuleType.AlwaysOnTop: return generalSettingsConfig.Enabled.AlwaysOnTop;
|
||||
case ModuleType.Awake: return generalSettingsConfig.Enabled.Awake;
|
||||
case ModuleType.CmdPal: return generalSettingsConfig.Enabled.CmdPal;
|
||||
case ModuleType.ColorPicker: return generalSettingsConfig.Enabled.ColorPicker;
|
||||
case ModuleType.CropAndLock: return generalSettingsConfig.Enabled.CropAndLock;
|
||||
case ModuleType.CursorWrap: return generalSettingsConfig.Enabled.CursorWrap;
|
||||
case ModuleType.LightSwitch: return generalSettingsConfig.Enabled.LightSwitch;
|
||||
case ModuleType.EnvironmentVariables: return generalSettingsConfig.Enabled.EnvironmentVariables;
|
||||
case ModuleType.FancyZones: return generalSettingsConfig.Enabled.FancyZones;
|
||||
case ModuleType.FileLocksmith: return generalSettingsConfig.Enabled.FileLocksmith;
|
||||
case ModuleType.FindMyMouse: return generalSettingsConfig.Enabled.FindMyMouse;
|
||||
case ModuleType.Hosts: return generalSettingsConfig.Enabled.Hosts;
|
||||
case ModuleType.ImageResizer: return generalSettingsConfig.Enabled.ImageResizer;
|
||||
case ModuleType.KeyboardManager: return generalSettingsConfig.Enabled.KeyboardManager;
|
||||
case ModuleType.MouseHighlighter: return generalSettingsConfig.Enabled.MouseHighlighter;
|
||||
case ModuleType.MouseJump: return generalSettingsConfig.Enabled.MouseJump;
|
||||
case ModuleType.MousePointerCrosshairs: return generalSettingsConfig.Enabled.MousePointerCrosshairs;
|
||||
case ModuleType.MouseWithoutBorders: return generalSettingsConfig.Enabled.MouseWithoutBorders;
|
||||
case ModuleType.NewPlus: return generalSettingsConfig.Enabled.NewPlus;
|
||||
case ModuleType.Peek: return generalSettingsConfig.Enabled.Peek;
|
||||
case ModuleType.PowerRename: return generalSettingsConfig.Enabled.PowerRename;
|
||||
case ModuleType.PowerLauncher: return generalSettingsConfig.Enabled.PowerLauncher;
|
||||
case ModuleType.PowerAccent: return generalSettingsConfig.Enabled.PowerAccent;
|
||||
case ModuleType.Workspaces: return generalSettingsConfig.Enabled.Workspaces;
|
||||
case ModuleType.RegistryPreview: return generalSettingsConfig.Enabled.RegistryPreview;
|
||||
case ModuleType.MeasureTool: return generalSettingsConfig.Enabled.MeasureTool;
|
||||
case ModuleType.ShortcutGuide: return generalSettingsConfig.Enabled.ShortcutGuide;
|
||||
case ModuleType.PowerOCR: return generalSettingsConfig.Enabled.PowerOcr;
|
||||
case ModuleType.ZoomIt: return generalSettingsConfig.Enabled.ZoomIt;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void SetIsModuleEnabled(GeneralSettings generalSettingsConfig, ModuleType moduleType, bool isEnabled)
|
||||
{
|
||||
switch (moduleType)
|
||||
{
|
||||
case ModuleType.AdvancedPaste: generalSettingsConfig.Enabled.AdvancedPaste = isEnabled; break;
|
||||
case ModuleType.AlwaysOnTop: generalSettingsConfig.Enabled.AlwaysOnTop = isEnabled; break;
|
||||
case ModuleType.Awake: generalSettingsConfig.Enabled.Awake = isEnabled; break;
|
||||
case ModuleType.CmdPal: generalSettingsConfig.Enabled.CmdPal = isEnabled; break;
|
||||
case ModuleType.ColorPicker: generalSettingsConfig.Enabled.ColorPicker = isEnabled; break;
|
||||
case ModuleType.CropAndLock: generalSettingsConfig.Enabled.CropAndLock = isEnabled; break;
|
||||
case ModuleType.CursorWrap: generalSettingsConfig.Enabled.CursorWrap = isEnabled; break;
|
||||
case ModuleType.LightSwitch: generalSettingsConfig.Enabled.LightSwitch = isEnabled; break;
|
||||
case ModuleType.EnvironmentVariables: generalSettingsConfig.Enabled.EnvironmentVariables = isEnabled; break;
|
||||
case ModuleType.FancyZones: generalSettingsConfig.Enabled.FancyZones = isEnabled; break;
|
||||
case ModuleType.FileLocksmith: generalSettingsConfig.Enabled.FileLocksmith = isEnabled; break;
|
||||
case ModuleType.FindMyMouse: generalSettingsConfig.Enabled.FindMyMouse = isEnabled; break;
|
||||
case ModuleType.Hosts: generalSettingsConfig.Enabled.Hosts = isEnabled; break;
|
||||
case ModuleType.ImageResizer: generalSettingsConfig.Enabled.ImageResizer = isEnabled; break;
|
||||
case ModuleType.KeyboardManager: generalSettingsConfig.Enabled.KeyboardManager = isEnabled; break;
|
||||
case ModuleType.MouseHighlighter: generalSettingsConfig.Enabled.MouseHighlighter = isEnabled; break;
|
||||
case ModuleType.MouseJump: generalSettingsConfig.Enabled.MouseJump = isEnabled; break;
|
||||
case ModuleType.MousePointerCrosshairs: generalSettingsConfig.Enabled.MousePointerCrosshairs = isEnabled; break;
|
||||
case ModuleType.MouseWithoutBorders: generalSettingsConfig.Enabled.MouseWithoutBorders = isEnabled; break;
|
||||
case ModuleType.NewPlus: generalSettingsConfig.Enabled.NewPlus = isEnabled; break;
|
||||
case ModuleType.Peek: generalSettingsConfig.Enabled.Peek = isEnabled; break;
|
||||
case ModuleType.PowerRename: generalSettingsConfig.Enabled.PowerRename = isEnabled; break;
|
||||
case ModuleType.PowerLauncher: generalSettingsConfig.Enabled.PowerLauncher = isEnabled; break;
|
||||
case ModuleType.PowerAccent: generalSettingsConfig.Enabled.PowerAccent = isEnabled; break;
|
||||
case ModuleType.Workspaces: generalSettingsConfig.Enabled.Workspaces = isEnabled; break;
|
||||
case ModuleType.RegistryPreview: generalSettingsConfig.Enabled.RegistryPreview = isEnabled; break;
|
||||
case ModuleType.MeasureTool: generalSettingsConfig.Enabled.MeasureTool = isEnabled; break;
|
||||
case ModuleType.ShortcutGuide: generalSettingsConfig.Enabled.ShortcutGuide = isEnabled; break;
|
||||
case ModuleType.PowerOCR: generalSettingsConfig.Enabled.PowerOcr = isEnabled; break;
|
||||
case ModuleType.ZoomIt: generalSettingsConfig.Enabled.ZoomIt = isEnabled; break;
|
||||
}
|
||||
}
|
||||
|
||||
public static GpoRuleConfigured GetModuleGpoConfiguration(ModuleType moduleType)
|
||||
{
|
||||
switch (moduleType)
|
||||
{
|
||||
case ModuleType.AdvancedPaste: return GPOWrapper.GetConfiguredAdvancedPasteEnabledValue();
|
||||
case ModuleType.AlwaysOnTop: return GPOWrapper.GetConfiguredAlwaysOnTopEnabledValue();
|
||||
case ModuleType.Awake: return GPOWrapper.GetConfiguredAwakeEnabledValue();
|
||||
case ModuleType.CmdPal: return GPOWrapper.GetConfiguredCmdPalEnabledValue();
|
||||
case ModuleType.ColorPicker: return GPOWrapper.GetConfiguredColorPickerEnabledValue();
|
||||
case ModuleType.CropAndLock: return GPOWrapper.GetConfiguredCropAndLockEnabledValue();
|
||||
case ModuleType.CursorWrap: return GPOWrapper.GetConfiguredCursorWrapEnabledValue();
|
||||
case ModuleType.EnvironmentVariables: return GPOWrapper.GetConfiguredEnvironmentVariablesEnabledValue();
|
||||
case ModuleType.FancyZones: return GPOWrapper.GetConfiguredFancyZonesEnabledValue();
|
||||
case ModuleType.FileLocksmith: return GPOWrapper.GetConfiguredFileLocksmithEnabledValue();
|
||||
case ModuleType.FindMyMouse: return GPOWrapper.GetConfiguredFindMyMouseEnabledValue();
|
||||
case ModuleType.Hosts: return GPOWrapper.GetConfiguredHostsFileEditorEnabledValue();
|
||||
case ModuleType.ImageResizer: return GPOWrapper.GetConfiguredImageResizerEnabledValue();
|
||||
case ModuleType.KeyboardManager: return GPOWrapper.GetConfiguredKeyboardManagerEnabledValue();
|
||||
case ModuleType.MouseHighlighter: return GPOWrapper.GetConfiguredMouseHighlighterEnabledValue();
|
||||
case ModuleType.MouseJump: return GPOWrapper.GetConfiguredMouseJumpEnabledValue();
|
||||
case ModuleType.MousePointerCrosshairs: return GPOWrapper.GetConfiguredMousePointerCrosshairsEnabledValue();
|
||||
case ModuleType.MouseWithoutBorders: return GPOWrapper.GetConfiguredMouseWithoutBordersEnabledValue();
|
||||
case ModuleType.NewPlus: return GPOWrapper.GetConfiguredNewPlusEnabledValue();
|
||||
case ModuleType.Peek: return GPOWrapper.GetConfiguredPeekEnabledValue();
|
||||
case ModuleType.PowerRename: return GPOWrapper.GetConfiguredPowerRenameEnabledValue();
|
||||
case ModuleType.PowerLauncher: return GPOWrapper.GetConfiguredPowerLauncherEnabledValue();
|
||||
case ModuleType.PowerAccent: return GPOWrapper.GetConfiguredQuickAccentEnabledValue();
|
||||
case ModuleType.Workspaces: return GPOWrapper.GetConfiguredWorkspacesEnabledValue();
|
||||
case ModuleType.RegistryPreview: return GPOWrapper.GetConfiguredRegistryPreviewEnabledValue();
|
||||
case ModuleType.MeasureTool: return GPOWrapper.GetConfiguredScreenRulerEnabledValue();
|
||||
case ModuleType.ShortcutGuide: return GPOWrapper.GetConfiguredShortcutGuideEnabledValue();
|
||||
case ModuleType.PowerOCR: return GPOWrapper.GetConfiguredTextExtractorEnabledValue();
|
||||
case ModuleType.ZoomIt: return GPOWrapper.GetConfiguredZoomItEnabledValue();
|
||||
default: return GpoRuleConfigured.Unavailable;
|
||||
}
|
||||
}
|
||||
|
||||
public static System.Type GetModulePageType(ModuleType moduleType)
|
||||
{
|
||||
return moduleType switch
|
||||
{
|
||||
ModuleType.AdvancedPaste => typeof(AdvancedPastePage),
|
||||
ModuleType.AlwaysOnTop => typeof(AlwaysOnTopPage),
|
||||
ModuleType.Awake => typeof(AwakePage),
|
||||
ModuleType.CmdPal => typeof(CmdPalPage),
|
||||
ModuleType.ColorPicker => typeof(ColorPickerPage),
|
||||
ModuleType.CropAndLock => typeof(CropAndLockPage),
|
||||
ModuleType.CursorWrap => typeof(MouseUtilsPage),
|
||||
ModuleType.LightSwitch => typeof(LightSwitchPage),
|
||||
ModuleType.EnvironmentVariables => typeof(EnvironmentVariablesPage),
|
||||
ModuleType.FancyZones => typeof(FancyZonesPage),
|
||||
ModuleType.FileLocksmith => typeof(FileLocksmithPage),
|
||||
ModuleType.FindMyMouse => typeof(MouseUtilsPage),
|
||||
ModuleType.Hosts => typeof(HostsPage),
|
||||
ModuleType.ImageResizer => typeof(ImageResizerPage),
|
||||
ModuleType.KeyboardManager => typeof(KeyboardManagerPage),
|
||||
ModuleType.MouseHighlighter => typeof(MouseUtilsPage),
|
||||
ModuleType.MouseJump => typeof(MouseUtilsPage),
|
||||
ModuleType.MousePointerCrosshairs => typeof(MouseUtilsPage),
|
||||
ModuleType.MouseWithoutBorders => typeof(MouseWithoutBordersPage),
|
||||
ModuleType.NewPlus => typeof(NewPlusPage),
|
||||
ModuleType.Peek => typeof(PeekPage),
|
||||
ModuleType.PowerRename => typeof(PowerRenamePage),
|
||||
ModuleType.PowerLauncher => typeof(PowerLauncherPage),
|
||||
ModuleType.PowerAccent => typeof(PowerAccentPage),
|
||||
ModuleType.Workspaces => typeof(WorkspacesPage),
|
||||
ModuleType.RegistryPreview => typeof(RegistryPreviewPage),
|
||||
ModuleType.MeasureTool => typeof(MeasureToolPage),
|
||||
ModuleType.ShortcutGuide => typeof(ShortcutGuidePage),
|
||||
ModuleType.PowerOCR => typeof(PowerOcrPage),
|
||||
ModuleType.ZoomIt => typeof(ZoomItPage),
|
||||
_ => typeof(DashboardPage), // never called, all values listed above
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,6 +124,7 @@
|
||||
<ProjectReference Include="..\..\common\LanguageModelProvider\LanguageModelProvider.csproj" />
|
||||
<ProjectReference Include="..\..\modules\MouseUtils\MouseJump.Common\MouseJump.Common.csproj" />
|
||||
<ProjectReference Include="..\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||
<ProjectReference Include="..\Settings.UI.Controls\Settings.UI.Controls.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- XamlIndexBuilder now outputs directly to Assets\Settings -->
|
||||
|
||||
@@ -41,16 +41,14 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
IsUserAdmin,
|
||||
ShowOobeWindow,
|
||||
ShowScoobeWindow,
|
||||
ShowFlyout,
|
||||
ContainsSettingsWindow,
|
||||
ContainsFlyoutPosition,
|
||||
}
|
||||
|
||||
private const int RequiredArgumentsSetSettingQty = 4;
|
||||
private const int RequiredArgumentsSetAdditionalSettingsQty = 4;
|
||||
private const int RequiredArgumentsGetSettingQty = 3;
|
||||
|
||||
private const int RequiredArgumentsLaunchedFromRunnerQty = 12;
|
||||
private const int RequiredArgumentsLaunchedFromRunnerQty = 10;
|
||||
|
||||
// Create an instance of the IPC wrapper.
|
||||
private static TwoWayPipeMessageIPCManaged ipcmanager;
|
||||
@@ -63,8 +61,6 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
|
||||
public bool ShowOobe { get; set; }
|
||||
|
||||
public bool ShowFlyout { get; set; }
|
||||
|
||||
public bool ShowScoobe { get; set; }
|
||||
|
||||
public Type StartupPage { get; set; } = typeof(Views.DashboardPage);
|
||||
@@ -194,9 +190,7 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
IsUserAnAdmin = cmdArgs[(int)Arguments.IsUserAdmin] == "true";
|
||||
ShowOobe = cmdArgs[(int)Arguments.ShowOobeWindow] == "true";
|
||||
ShowScoobe = cmdArgs[(int)Arguments.ShowScoobeWindow] == "true";
|
||||
ShowFlyout = cmdArgs[(int)Arguments.ShowFlyout] == "true";
|
||||
bool containsSettingsWindow = cmdArgs[(int)Arguments.ContainsSettingsWindow] == "true";
|
||||
bool containsFlyoutPosition = cmdArgs[(int)Arguments.ContainsFlyoutPosition] == "true";
|
||||
|
||||
// To keep track of variable arguments
|
||||
int currentArgumentIndex = RequiredArgumentsLaunchedFromRunnerQty;
|
||||
@@ -209,15 +203,6 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
currentArgumentIndex++;
|
||||
}
|
||||
|
||||
int flyout_x = 0;
|
||||
int flyout_y = 0;
|
||||
if (containsFlyoutPosition)
|
||||
{
|
||||
// get the flyout position arguments
|
||||
_ = int.TryParse(cmdArgs[currentArgumentIndex++], out flyout_x);
|
||||
_ = int.TryParse(cmdArgs[currentArgumentIndex++], out flyout_y);
|
||||
}
|
||||
|
||||
RunnerHelper.WaitForPowerToysRunner(PowerToysPID, () =>
|
||||
{
|
||||
Environment.Exit(0);
|
||||
@@ -238,7 +223,7 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
return 0;
|
||||
});
|
||||
|
||||
if (!ShowOobe && !ShowScoobe && !ShowFlyout)
|
||||
if (!ShowOobe && !ShowScoobe)
|
||||
{
|
||||
settingsWindow = new MainWindow();
|
||||
settingsWindow.Activate();
|
||||
@@ -278,16 +263,6 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
WindowHelpers.ForceTopBorder1PixelInsetOnWindows10(WindowNative.GetWindowHandle(settingsWindow));
|
||||
SetOobeWindow(scoobeWindow);
|
||||
}
|
||||
else if (ShowFlyout)
|
||||
{
|
||||
POINT? p = null;
|
||||
if (containsFlyoutPosition)
|
||||
{
|
||||
p = new POINT(flyout_x, flyout_y);
|
||||
}
|
||||
|
||||
ShellPage.OpenFlyoutCallback(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -364,7 +339,6 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
|
||||
private static MainWindow settingsWindow;
|
||||
private static OobeWindow oobeWindow;
|
||||
private static FlyoutWindow flyoutWindow;
|
||||
|
||||
public static void ClearSettingsWindow()
|
||||
{
|
||||
@@ -381,31 +355,16 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
return oobeWindow;
|
||||
}
|
||||
|
||||
public static FlyoutWindow GetFlyoutWindow()
|
||||
{
|
||||
return flyoutWindow;
|
||||
}
|
||||
|
||||
public static void SetOobeWindow(OobeWindow window)
|
||||
{
|
||||
oobeWindow = window;
|
||||
}
|
||||
|
||||
public static void SetFlyoutWindow(FlyoutWindow window)
|
||||
{
|
||||
flyoutWindow = window;
|
||||
}
|
||||
|
||||
public static void ClearOobeWindow()
|
||||
{
|
||||
oobeWindow = null;
|
||||
}
|
||||
|
||||
public static void ClearFlyoutWindow()
|
||||
{
|
||||
flyoutWindow = null;
|
||||
}
|
||||
|
||||
public static Type GetPage(string settingWindow)
|
||||
{
|
||||
switch (settingWindow)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user