mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-01-01 09:56:32 +01:00
Compare commits
2 Commits
dev/addNew
...
stefan/tes
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca7cee4ba4 | ||
|
|
d80e11f5d9 |
4
.github/actions/spell-check/allow/names.txt
vendored
4
.github/actions/spell-check/allow/names.txt
vendored
@@ -71,7 +71,6 @@ Garside
|
||||
Gershaft
|
||||
Giordani
|
||||
Gokce
|
||||
grzhan
|
||||
Guo
|
||||
hanselman
|
||||
Harmath
|
||||
@@ -145,7 +144,6 @@ TBM
|
||||
tilovell
|
||||
Triet
|
||||
waaverecords
|
||||
Whuihuan
|
||||
Xpg
|
||||
ycv
|
||||
Yuniardi
|
||||
@@ -156,8 +154,6 @@ Zykova
|
||||
|
||||
# OTHERS
|
||||
|
||||
Bilibili
|
||||
BVID
|
||||
cmdow
|
||||
Controlz
|
||||
cortana
|
||||
|
||||
37
.github/actions/spell-check/expect.txt
vendored
37
.github/actions/spell-check/expect.txt
vendored
@@ -69,9 +69,11 @@ AQS
|
||||
ARandom
|
||||
ARCHITEW
|
||||
ARemapped
|
||||
ari
|
||||
ARPINSTALLLOCATION
|
||||
ARPPRODUCTICON
|
||||
ARRAYSIZE
|
||||
arw
|
||||
asf
|
||||
AShortcut
|
||||
ASingle
|
||||
@@ -102,7 +104,6 @@ backtracer
|
||||
bbwe
|
||||
bck
|
||||
BESTEFFORT
|
||||
bezelled
|
||||
bhid
|
||||
BIF
|
||||
bigbar
|
||||
@@ -150,7 +151,6 @@ CALG
|
||||
callbackptr
|
||||
calpwstr
|
||||
Cangjie
|
||||
caniuse
|
||||
CANRENAME
|
||||
CAPTUREBLT
|
||||
CAPTURECHANGED
|
||||
@@ -259,7 +259,7 @@ CRH
|
||||
critsec
|
||||
Crossdevice
|
||||
CRSEL
|
||||
crx
|
||||
crw
|
||||
CSearch
|
||||
CSettings
|
||||
cso
|
||||
@@ -301,8 +301,8 @@ DCOM
|
||||
dcommon
|
||||
dcomp
|
||||
DComposition
|
||||
DCR
|
||||
DCs
|
||||
dcr
|
||||
dcs
|
||||
ddd
|
||||
DDEIf
|
||||
DDevice
|
||||
@@ -372,6 +372,7 @@ DRAWCLIPBOARD
|
||||
DRAWFRAME
|
||||
drawingcolor
|
||||
dreamsofameaningfullife
|
||||
drf
|
||||
drivedetectionwarning
|
||||
dshow
|
||||
DSTINVERT
|
||||
@@ -415,6 +416,7 @@ editkeyboardwindow
|
||||
EDITSHORTCUTS
|
||||
editshortcutswindow
|
||||
EFile
|
||||
eip
|
||||
ekus
|
||||
emmintrin
|
||||
Emoji
|
||||
@@ -588,7 +590,7 @@ Hiberboot
|
||||
HIBYTE
|
||||
hicon
|
||||
HIDEWINDOW
|
||||
Hif
|
||||
hif
|
||||
HIMAGELIST
|
||||
himl
|
||||
hinst
|
||||
@@ -637,7 +639,6 @@ HWNDLAST
|
||||
HWNDNEXT
|
||||
HWNDPREV
|
||||
hyjiacan
|
||||
IApp
|
||||
IBeam
|
||||
ICapture
|
||||
IClass
|
||||
@@ -664,6 +665,7 @@ IGNOREUNKNOWN
|
||||
IGraphics
|
||||
iid
|
||||
Iindex
|
||||
iiq
|
||||
IJson
|
||||
Ijwhost
|
||||
IKs
|
||||
@@ -726,17 +728,17 @@ ISSEPARATOR
|
||||
ITask
|
||||
ith
|
||||
ITHUMBNAIL
|
||||
ITwoWayPipeMessageIPCManaged
|
||||
IUI
|
||||
IUnknown
|
||||
IUse
|
||||
IWbem
|
||||
IWeb
|
||||
IWIC
|
||||
iwr
|
||||
IYUV
|
||||
jfi
|
||||
jfif
|
||||
jgeosdfsdsgmkedfgdfgdfgbkmhcgcflmi
|
||||
jif
|
||||
jjw
|
||||
jobject
|
||||
jpe
|
||||
@@ -745,6 +747,7 @@ Jsons
|
||||
jsonval
|
||||
junja
|
||||
jxr
|
||||
kdc
|
||||
keybd
|
||||
KEYBDDATA
|
||||
KEYBDINPUT
|
||||
@@ -869,6 +872,7 @@ MAXIMIZEBOX
|
||||
MAXSHORTCUTSIZE
|
||||
maxversiontested
|
||||
MBR
|
||||
mdc
|
||||
MDICHILD
|
||||
MDL
|
||||
mdtext
|
||||
@@ -876,6 +880,7 @@ mdtxt
|
||||
mdwn
|
||||
MEDIASUBTYPE
|
||||
mediatype
|
||||
mef
|
||||
MENUITEMINFO
|
||||
MENUITEMINFOW
|
||||
MERGECOPY
|
||||
@@ -933,6 +938,7 @@ mpmc
|
||||
MRM
|
||||
MRT
|
||||
mru
|
||||
mrw
|
||||
msc
|
||||
mscorlib
|
||||
msdata
|
||||
@@ -997,7 +1003,6 @@ newitem
|
||||
newpath
|
||||
newplus
|
||||
NEWPLUSCONTEXTMENU
|
||||
NEWPLUSSHELLEXTENSIONWIN
|
||||
newrow
|
||||
newsgroups
|
||||
NIF
|
||||
@@ -1056,6 +1061,7 @@ NOZORDER
|
||||
NPH
|
||||
npmjs
|
||||
NResize
|
||||
nrw
|
||||
nsunt
|
||||
NTAPI
|
||||
ntdll
|
||||
@@ -1085,6 +1091,7 @@ opensource
|
||||
openxmlformats
|
||||
OPTIMIZEFORINVOKE
|
||||
ORAW
|
||||
ori
|
||||
ORPHANEDDIALOGTITLE
|
||||
ORSCANS
|
||||
oss
|
||||
@@ -1133,7 +1140,7 @@ pdo
|
||||
pdto
|
||||
pdtobj
|
||||
pdw
|
||||
peb
|
||||
Peb
|
||||
pef
|
||||
PElems
|
||||
Pels
|
||||
@@ -1169,7 +1176,6 @@ pnid
|
||||
Pnp
|
||||
Popups
|
||||
POPUPWINDOW
|
||||
POSITIONITEM
|
||||
POWERRENAMECONTEXTMENU
|
||||
powerrenameinput
|
||||
POWERRENAMETEST
|
||||
@@ -1264,6 +1270,7 @@ QUERYENDSESSION
|
||||
QUERYOPEN
|
||||
QUEUESYNC
|
||||
QUNS
|
||||
raf
|
||||
RAII
|
||||
RAlt
|
||||
Rasterize
|
||||
@@ -1367,6 +1374,8 @@ runtimes
|
||||
ruuid
|
||||
rvm
|
||||
rwin
|
||||
rwl
|
||||
rwz
|
||||
sacl
|
||||
safeprojectname
|
||||
SAMEKEYPREVIOUSLYMAPPED
|
||||
@@ -1500,6 +1509,7 @@ Srch
|
||||
SRCINVERT
|
||||
SRCPAINT
|
||||
SResize
|
||||
srf
|
||||
srme
|
||||
srre
|
||||
srw
|
||||
@@ -1574,8 +1584,6 @@ SYSLIB
|
||||
SYSMENU
|
||||
SYSTEMAPPS
|
||||
SYSTEMTIME
|
||||
SYSTEMWOW
|
||||
tailwindcss
|
||||
tapp
|
||||
TApplication
|
||||
TApplied
|
||||
@@ -1619,7 +1627,6 @@ tkconverters
|
||||
TLayout
|
||||
tlb
|
||||
tlbimp
|
||||
tlhelp
|
||||
TMPVAR
|
||||
TNP
|
||||
Toolhelp
|
||||
|
||||
@@ -181,7 +181,6 @@
|
||||
|
||||
"WinUI3Apps\\PowerToys.NewPlus.ShellExtension.dll",
|
||||
"WinUI3Apps\\NewPlusPackage.msix",
|
||||
"WinUI3Apps\\PowerToys.NewPlus.ShellExtension.win10.dll",
|
||||
|
||||
"PowerAccent.Core.dll",
|
||||
"PowerToys.PowerAccent.dll",
|
||||
@@ -202,7 +201,6 @@
|
||||
"PowerToys.WorkspacesLauncherUI.exe",
|
||||
"PowerToys.WorkspacesLauncherUI.dll",
|
||||
"PowerToys.WorkspacesModuleInterface.dll",
|
||||
"PowerToys.WorkspacesCsharpLibrary.dll",
|
||||
|
||||
"WinUI3Apps\\PowerToys.RegistryPreviewExt.dll",
|
||||
"WinUI3Apps\\PowerToys.RegistryPreviewUILib.dll",
|
||||
|
||||
@@ -1,53 +1,53 @@
|
||||
# PowerToys Contributor's Guide
|
||||
|
||||
Below is our guidance for reporting issues, proposing new features, and submitting contributions via Pull Requests (PRs). Our philosophy is to understand the problem and scenarios first, which is why we follow this pattern before work starts.
|
||||
Below is our guidance for how to report issues, propose new features, and submit contributions via Pull Requests (PRs). Our philosophy is heavily based around understanding the problem and scenarios first, this is why we follow this pattern before work has started.
|
||||
|
||||
1. There is an issue.
|
||||
2. There has been a conversation.
|
||||
3. There is agreement on the problem, the fit for PowerToys, and the solution to the problem (implementation).
|
||||
1. There is an issue
|
||||
2. There has been a conversation
|
||||
3. There is agreement on the problem, the fit for PowerToys, and the solution to the problem (implementation)
|
||||
|
||||
## Filing an Issue
|
||||
## Filing an issue
|
||||
|
||||
**Importance of Filing an Issue First**
|
||||
Please follow this simple rule to help us eliminate any unnecessary wasted effort & frustration, and ensure an efficient and effective use of everyone's time - yours, ours, and other community members':
|
||||
|
||||
Please follow this rule to help eliminate wasted effort and frustration, and to ensure an efficient and effective use of everyone’s time:
|
||||
> 👉 If you have a question, think you've discovered an issue, would like to propose a new feature, etc., then find/file an issue **BEFORE** starting work to fix/implement it.
|
||||
|
||||
> 👉 If you have a question, think you've discovered an issue, or would like to propose a new feature, please find/file an issue **BEFORE** starting work to fix/implement it.
|
||||
When requesting new features / enhancements, understanding problem and scenario around it is extremely important. Having additional evidence, data, tweets, blog posts, research, ... anything is extremely helpful. This information provides context to the scenario that may otherwise be lost.
|
||||
|
||||
When requesting new features or enhancements, providing additional evidence, data, tweets, blog posts, or research is extremely helpful. This information gives context to the scenario that may otherwise be lost.
|
||||
* Don't know whether you're reporting an issue or requesting a feature? File an issue
|
||||
* Have a question that you don't see answered in docs, videos, etc.? File an issue
|
||||
* Want to know if we're planning on building a particular feature? File an issue
|
||||
* Got a great idea for a new utility or feature? File an issue/request/idea
|
||||
* Don't understand how to do something? File an issue/Community Guidance Request
|
||||
* Found an existing issue that describes yours? Great - upvote and add additional commentary / info / repro-steps / etc.
|
||||
|
||||
* Unsure whether it’s an issue or feature request? File an issue.
|
||||
* Have a question that isn't answered in the docs, videos, etc.? File an issue.
|
||||
* Want to know if we’re planning a particular feature? File an issue.
|
||||
* Got a great idea for a new utility or feature? File an issue/request/idea.
|
||||
* Don’t understand how to do something? File an issue/Community Guidance Request.
|
||||
* Found an existing issue that describes yours? Great! Upvote and add additional commentary, info, or repro steps.
|
||||
A quick search before filing an issue also could be helpful. It is likely someone else has found the problem you're seeing, and someone may be working on or have already contributed a fix!
|
||||
|
||||
A quick search before filing an issue could be helpful. It’s likely someone else has found the same problem, and they may even be working on or have already contributed a fix!
|
||||
### How to tell the PowerToys team this is an interesting thing to focus on
|
||||
|
||||
### Indicating Interest in Issues
|
||||
|
||||
To let the team know which issues are important, upvote by clicking the [+😊] button and the 👍 icon on the original issue post. Avoid comments like "+1" or "me too" as they clutter the discussion and make it harder to prioritize requests.
|
||||
Upvote the original issue by clicking its [+😊] button and hitting 👍 (+1) icon or a different one. This way allows us to measure how impactful different issues are compared to others. The issue with comments like "+1", "me too", or similar is they actually make it harder to have a conversation and harder to quickly determine trending important requests.
|
||||
|
||||
---
|
||||
|
||||
## Contributing Fixes/Features
|
||||
## Contributing fixes / features
|
||||
|
||||
Please comment on our ["Would you like to contribute to PowerToys?"](https://github.com/microsoft/PowerToys/issues/28769) thread to let us know you're interested in working on something before you start. This helps avoid multiple people unexpectedly working on the same thing and ensures everyone is clear on what should be done. It's less work for everyone to establish this up front.
|
||||
Please comment on [our "Would you like to contribute to PowerToys?" thread](https://github.com/microsoft/PowerToys/issues/28769) to let us know you're interested in working on something before you start the work. Not only does this avoid multiple people unexpectedly working on the same thing at the same time but it enables us to make sure everyone is clear on what should be done to implement any new functionality. It's less work for everyone, in the long run, to establish this up front.
|
||||
|
||||
### Localization Issues
|
||||
### Localization issues
|
||||
|
||||
For localization issues, please file an issue to notify our internal localization team, as community PRs for localization aren't accepted. Localization is handled exclusively by the internal Microsoft team.
|
||||
Please file localization issues, so our internal localization team can identify and fix them. However we currently don't accept community Pull Requests fixing localization issues. Localization is handled by the internal Microsoft team only.
|
||||
|
||||
### To Spec or Not to Spec
|
||||
### To Spec or not to Spec
|
||||
|
||||
A key point is for everyone to understand the approach that will be taken. We want to be sure that any work done will be accepted. Larger-scope items will require a spec to outline the approach and allow for discussion. Specs help collaborators consider different solutions, describe feature behavior, and plan for errors. Achieving agreement in a spec before writing code often results in simpler code and less wasted effort.
|
||||
A key point is for everyone to understand the approach that will be taken. We want to be sure if anyone does work, we will accept it in. Items that are larger in scope we'll want some type of spec to understand what is being planned and have a discussion. Specs help collaborators discuss different approaches to solve a problem, describe how the feature will behave, how the feature will impact the user, what happens if something goes wrong, etc. Driving towards agreement in a spec, before any code is written, often results in simpler code, and less wasted effort in the long run.
|
||||
|
||||
Once a team member has agreed with your approach, proceed to the "Development" section below. Team members are happy to help review specs and guide them to completion.
|
||||
For such scenarios, once a team member has agreed with your approach, skip ahead to the section headed "Development" section below.
|
||||
|
||||
Team members will be happy to help review specs and guide them to completion.
|
||||
|
||||
### Help Wanted
|
||||
|
||||
Once the team has approved an issue/spec approach, development can proceed. If no developers are immediately available, the spec may be parked and labeled "Help Wanted," ready for a developer to get started. For development opportunities, visit [Issues labeled Help Wanted](https://github.com/microsoft/PowerToys/labels/Help%20Wanted).
|
||||
Once the team has approved an issue/spec approach to solving, development can proceed. If no developers are immediately available, the spec can be parked ready for a developer to get started. Parked specs' issues will be labeled "Help Wanted". To find a list of development opportunities waiting for developer involvement, visit the Issues and filter on [the Help-Wanted label](https://github.com/microsoft/PowerToys/labels/Help%20Wanted).
|
||||
|
||||
---
|
||||
|
||||
@@ -55,18 +55,18 @@ Once the team has approved an issue/spec approach, development can proceed. If n
|
||||
|
||||
Follow the [development guidelines](https://github.com/microsoft/PowerToys/blob/main/doc/devdocs/readme.md).
|
||||
|
||||
### Naming Features and Functionality
|
||||
### Naming of features and functionality
|
||||
|
||||
Names should be descriptive and straightforward, clearly reflecting functionality and usefulness.
|
||||
Naming should be descriptive and straight forward. We want names to be clear about functionality and usefulness moving forward.
|
||||
|
||||
### Becoming a Collaborator on the PowerToys Team
|
||||
### How can I become a collaborator on the PowerToys team
|
||||
|
||||
Be an active community member! Make helpful contributions by filing bugs, offering suggestions, developing fixes and features, conducting code reviews, and updating documentation.
|
||||
Be a great community member. Just help out a lot and make useful additions, filing bugs/suggestions, help develop fixes and features, code reviews, and always, docs. Lets continue to make the PowerToys repository a great spot to learn and make a great set of utilities.
|
||||
|
||||
When the time comes, Microsoft will reach out to you about becoming a formal team member. Just make sure they have a way to contact you. 😊
|
||||
When the time comes, Microsoft will reach out and help make you a formal team member. Just make sure they can reach out to you :)
|
||||
|
||||
---
|
||||
|
||||
## Thank You
|
||||
## Thank you
|
||||
|
||||
Thank you in advance for your contribution! We appreciate your help in making PowerToys a better tool for everyone.
|
||||
Thank you in advance for your contribution!
|
||||
|
||||
@@ -67,10 +67,6 @@ _If you want to find diagnostic data events in the source code, these two links
|
||||
<td>Microsoft.PowerToys.TrayFlyoutModuleRunEvent</td>
|
||||
<td>Logs when a utility from the tray flyout menu is run.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Microsoft.PowerToys.Uninstall_Success</td>
|
||||
<td>Logs when PowerToys is successfully uninstalled (who would do such a thing!).</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### OOBE (Out-of-box experience)
|
||||
@@ -143,10 +139,6 @@ _If you want to find diagnostic data events in the source code, these two links
|
||||
<td>Microsoft.PowerToys.AdvancedPasteInAppKeyboardShortcutEvent</td>
|
||||
<td>Triggered when a keyboard shortcut is used within the Advanced Paste interface.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Microsoft.PowerToys.AdvancedPasteSemanticKernelFormatEvent</td>
|
||||
<td>Triggered when Advanced Paste leverages the Semantic Kernel.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### Always on Top
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
-->
|
||||
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.1.5" />
|
||||
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.6.241114003" />
|
||||
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.6.240923002" />
|
||||
<PackageVersion Include="Microsoft.Xaml.Behaviors.WinUI.Managed" Version="2.0.9" />
|
||||
<PackageVersion Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
|
||||
<PackageVersion Include="ModernWpfUI" Version="0.9.4" />
|
||||
|
||||
@@ -1333,7 +1333,7 @@ EXHIBIT A -Mozilla Public License.
|
||||
- Microsoft.Windows.CsWin32 0.2.46-beta
|
||||
- Microsoft.Windows.CsWinRT 2.1.5
|
||||
- Microsoft.Windows.SDK.BuildTools 10.0.22621.2428
|
||||
- Microsoft.WindowsAppSDK 1.6.241114003
|
||||
- Microsoft.WindowsAppSDK 1.6.240923002
|
||||
- Microsoft.Xaml.Behaviors.WinUI.Managed 2.0.9
|
||||
- Microsoft.Xaml.Behaviors.Wpf 1.1.39
|
||||
- ModernWpfUI 0.9.4
|
||||
|
||||
@@ -630,10 +630,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EtwTrace", "src\common\Tele
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseWithoutBorders.UnitTests", "src\modules\MouseWithoutBorders\MouseWithoutBorders.UnitTests\MouseWithoutBorders.UnitTests.csproj", "{66614C26-314C-4B91-9071-76133422CFEF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkspacesCsharpLibrary", "src\modules\Workspaces\WorkspacesCsharpLibrary\WorkspacesCsharpLibrary.csproj", "{89D0E199-B17A-418C-B2F8-7375B6708357}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NewPlus.ShellExtension.win10", "src\modules\NewPlus\NewShellExtensionContextMenu.win10\NewPlus.ShellExtension.win10.vcxproj", "{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|ARM64 = Debug|ARM64
|
||||
@@ -2250,30 +2246,6 @@ Global
|
||||
{8A08D663-4995-40E3-B42C-3F910625F284}.Release|x64.Build.0 = Release|x64
|
||||
{8A08D663-4995-40E3-B42C-3F910625F284}.Release|x86.ActiveCfg = Release|x64
|
||||
{8A08D663-4995-40E3-B42C-3F910625F284}.Release|x86.Build.0 = Release|x64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x64.Build.0 = Debug|x64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x86.Build.0 = Debug|x64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x64.ActiveCfg = Release|x64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x64.Build.0 = Release|x64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x86.ActiveCfg = Release|x64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x86.Build.0 = Release|x64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x64.Build.0 = Debug|x64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x86.Build.0 = Debug|x64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x64.ActiveCfg = Release|x64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x64.Build.0 = Release|x64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x86.ActiveCfg = Release|x64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x86.Build.0 = Release|x64
|
||||
{D962A009-834F-4EEC-AABB-430DF8F98E39}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{D962A009-834F-4EEC-AABB-430DF8F98E39}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{D962A009-834F-4EEC-AABB-430DF8F98E39}.Debug|x64.ActiveCfg = Debug|x64
|
||||
@@ -2674,6 +2646,30 @@ Global
|
||||
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|x64.Build.0 = Release|x64
|
||||
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|x86.ActiveCfg = Release|x64
|
||||
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|x86.Build.0 = Release|x64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x64.Build.0 = Debug|x64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x86.Build.0 = Debug|x64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x64.ActiveCfg = Release|x64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x64.Build.0 = Release|x64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x86.ActiveCfg = Release|x64
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x86.Build.0 = Release|x64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x64.Build.0 = Debug|x64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x86.Build.0 = Debug|x64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x64.ActiveCfg = Release|x64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x64.Build.0 = Release|x64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x86.ActiveCfg = Release|x64
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x86.Build.0 = Release|x64
|
||||
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|x64.ActiveCfg = Debug|x64
|
||||
@@ -2782,30 +2778,6 @@ Global
|
||||
{66614C26-314C-4B91-9071-76133422CFEF}.Release|x64.Build.0 = Release|x64
|
||||
{66614C26-314C-4B91-9071-76133422CFEF}.Release|x86.ActiveCfg = Release|x64
|
||||
{66614C26-314C-4B91-9071-76133422CFEF}.Release|x86.Build.0 = Release|x64
|
||||
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|x64.Build.0 = Debug|x64
|
||||
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{89D0E199-B17A-418C-B2F8-7375B6708357}.Debug|x86.Build.0 = Debug|x64
|
||||
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x64.ActiveCfg = Release|x64
|
||||
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x64.Build.0 = Release|x64
|
||||
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x86.ActiveCfg = Release|x64
|
||||
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x86.Build.0 = Release|x64
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|x64.Build.0 = Debug|x64
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|x86.Build.0 = Debug|x64
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x64.ActiveCfg = Release|x64
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x64.Build.0 = Release|x64
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x86.ActiveCfg = Release|x64
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x86.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -2984,8 +2956,6 @@ Global
|
||||
{B5EB9FE9-37EF-47C3-B8B8-81AD3C2972C2} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC}
|
||||
{A663E672-B26D-4EC0-BEAB-FE2E424AC46F} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC}
|
||||
{8A08D663-4995-40E3-B42C-3F910625F284} = {322566EF-20DC-43A6-B9F8-616AF942579A}
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA} = {322566EF-20DC-43A6-B9F8-616AF942579A}
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77} = {322566EF-20DC-43A6-B9F8-616AF942579A}
|
||||
{D962A009-834F-4EEC-AABB-430DF8F98E39} = {322566EF-20DC-43A6-B9F8-616AF942579A}
|
||||
{9873BA05-4C41-4819-9283-CF45D795431B} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
|
||||
{FC373B24-3293-453C-AAF5-CF2909DCEE6A} = {9873BA05-4C41-4819-9283-CF45D795431B}
|
||||
@@ -3025,6 +2995,8 @@ Global
|
||||
{8ACB33D9-C95B-47D4-8363-9731EE0930A0} = {CA716AE6-FE5C-40AC-BB8F-2C87912687AC}
|
||||
{CA716AE6-FE5C-40AC-BB8F-2C87912687AC} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
|
||||
{F055103B-F80B-4D0C-BF48-057C55620033} = {5A7818A8-109C-4E1C-850D-1A654E234B0E}
|
||||
{923DF87C-CA99-4D1C-B1D2-959174E95BFA} = {322566EF-20DC-43A6-B9F8-616AF942579A}
|
||||
{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77} = {322566EF-20DC-43A6-B9F8-616AF942579A}
|
||||
{A2221D7E-55E7-4BEA-90D1-4F162D670BBF} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
|
||||
{BE126CBB-AE12-406A-9837-A05ACFCA57A7} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
|
||||
{14CB58B7-D280-4A7A-95DE-4B2DF14EA000} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
|
||||
@@ -3037,8 +3009,6 @@ Global
|
||||
{37D07516-4185-43A4-924F-3C7A5D95ECF6} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
|
||||
{8F021B46-362B-485C-BFBA-CCF83E820CBD} = {8F62026A-294B-41C6-8839-87463613F216}
|
||||
{66614C26-314C-4B91-9071-76133422CFEF} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC}
|
||||
{89D0E199-B17A-418C-B2F8-7375B6708357} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
|
||||
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E} = {CA716AE6-FE5C-40AC-BB8F-2C87912687AC}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
|
||||
|
||||
@@ -39,9 +39,6 @@ Contact the developers of a plugin directly for assistance with a specific plugi
|
||||
| [GitHubRepo](https://github.com/8LWXpg/PowerToysRun-GitHubRepo) | [8LWXpg](https://github.com/8LWXpg) | Search and open GitHub repositories |
|
||||
| [ProcessKiller](https://github.com/8LWXpg/PowerToysRun-ProcessKiller) | [8LWXpg](https://github.com/8LWXpg) | Search and kill processes |
|
||||
| [ChatGPT](https://github.com/ferraridavide/ChatGPTPowerToys) | [ferraridavide](https://github.com/ferraridavide) | Ask a question to ChatGPT |
|
||||
| [CanIUse](https://github.com/skttl/ptrun-caniuse) | [skttl](https://github.com/skttl) | Look up browser feature support with caniuse.com |
|
||||
| [TailwindCSS](https://github.com/skttl/ptrun-tailwindcss) | [skttl](https://github.com/skttl) | Search the documentation of TailwindCSS |
|
||||
| [HttpStatusCodes](https://github.com/grzhan/HttpStatusCodePowerToys) | [grzhan](https://github.com/grzhan) | Search for http status codes |
|
||||
|
||||
## Extending software plugins
|
||||
|
||||
@@ -61,4 +58,3 @@ Below are community created plugins that target a website or software. They are
|
||||
| [PowerSearch for 1Password](https://github.com/KairuDeibisu/PowerToysRunPlugin1Password) | [KairuDeibisu](https://github.com/KairuDeibisu) | An unofficial plugin for searching 1Password for usernames and passwords |
|
||||
| [HackMD](https://github.com/8LWXpg/PowerToysRun-HackMD) | [8LWXpg](https://github.com/8LWXpg) | Open HackMD notes |
|
||||
| [SSH](https://github.com/8LWXpg/PowerToysRun-SSH) | [8LWXpg](https://github.com/8LWXpg) | Connect to ssh clients |
|
||||
| [Bilibili](https://github.com/Whuihuan/PowerToysRun-Bilibili) | [Whuihuan](https://github.com/Whuihuan) | Use AVID or BVID to parse and jump to Bilibili |
|
||||
|
||||
@@ -18,19 +18,6 @@
|
||||
<DirectoryRef Id="NewPlusAssetsInstallFolder" FileSource="$(var.NewPlusAssetsFilesPath)">
|
||||
<!-- Generated by generateFileComponents.ps1 -->
|
||||
<!--NewPlusAssetsFiles_Component_Def-->
|
||||
|
||||
<!-- NewPlus Shell Extension for Win10 registration -->
|
||||
<Component Id="NewPlus_ShellExtension_win10" Guid="D5456D4A-6EEC-4B85-944D-6A6A4A74FFA6" Win64="yes">
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\CLSID\{FF90D477-E32A-4BE8-8CC5-A502A97F5401}">
|
||||
<RegistryValue Type="string" Value="NewPlus Shell Extension Win10" />
|
||||
<RegistryValue Type="string" Name="ContextMenuOptIn" Value="" />
|
||||
<RegistryValue Type="string" Key="InprocServer32" Value="[WinUI3AppsInstallFolder]PowerToys.NewPlus.ShellExtension.win10.dll" />
|
||||
<RegistryValue Type="string" Key="InprocServer32" Name="ThreadingModel" Value="Apartment" />
|
||||
</RegistryKey>
|
||||
<RegistryKey Root="$(var.RegistryScope)" Key="SOFTWARE\Classes\Directory\background\ShellEx\ContextMenuHandlers\NewPlusShellExtensionWin10">
|
||||
<RegistryValue Type="string" Value="{FF90D477-E32A-4BE8-8CC5-A502A97F5401}"/>
|
||||
</RegistryKey>
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
|
||||
<ComponentGroup Id="NewPlusComponentGroup">
|
||||
@@ -40,7 +27,6 @@
|
||||
</RegistryKey>
|
||||
<RemoveFolder Id="RemoveFolderNewPlusAssetsFolder" Directory="NewPlusAssetsInstallFolder" On="uninstall"/>
|
||||
</Component>
|
||||
<ComponentRef Id="NewPlus_ShellExtension_win10" />
|
||||
</ComponentGroup>
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ export async function registerAdditionalLanguages(monaco){
|
||||
await languageDefinitions();
|
||||
registerAdditionalLanguage("cppExt", [".ino", ".pde"], "cpp", monaco)
|
||||
registerAdditionalLanguage("xmlExt", [".wsdl", ".csproj", ".vcxproj", ".vbproj", ".fsproj"], "xml", monaco)
|
||||
registerAdditionalLanguage("txtExt", [".sln", ".log", ".vsconfig", ".env", ".srt", ".ahk"], "txt", monaco)
|
||||
registerAdditionalLanguage("txtExt", [".sln", ".log", ".vsconfig", ".env", ".srt"], "txt", monaco)
|
||||
registerAdditionalLanguage("razorExt", [".razor"], "razor", monaco)
|
||||
registerAdditionalLanguage("vbExt", [".vbs"], "vb", monaco)
|
||||
registerAdditionalLanguage("iniExt", [".inf", ".gitconfig", ".gitattributes", ".editorconfig"], "ini", monaco)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -29,28 +29,34 @@ namespace Microsoft.PowerToys.FilePreviewCommon
|
||||
new XmlFormatter(),
|
||||
}.AsReadOnly();
|
||||
|
||||
private static readonly Lazy<string> _monacoDirectory = new(GetRuntimeMonacoDirectory);
|
||||
private static string? _monacoDirectory;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path of the Monaco assets folder.
|
||||
/// </summary>
|
||||
public static string MonacoDirectory => _monacoDirectory.Value;
|
||||
|
||||
private static string GetRuntimeMonacoDirectory()
|
||||
public static string GetRuntimeMonacoDirectory()
|
||||
{
|
||||
string exePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? string.Empty;
|
||||
|
||||
// If the executable is within "WinUI3Apps", correct the path first.
|
||||
if (Path.GetFileName(exePath) == "WinUI3Apps")
|
||||
string codeBase = Assembly.GetExecutingAssembly().Location;
|
||||
string path = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(codeBase) ?? string.Empty, "Assets", "Monaco"));
|
||||
if (Path.Exists(path))
|
||||
{
|
||||
exePath = Path.Combine(exePath, "..");
|
||||
return path;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We're likely in WinUI3Apps directory and need to go back to the base directory.
|
||||
return Path.GetFullPath(Path.Combine(Path.GetDirectoryName(codeBase) ?? string.Empty, "..", "Assets", "Monaco"));
|
||||
}
|
||||
}
|
||||
|
||||
string monacoPath = Path.Combine(exePath, "Assets", "Monaco");
|
||||
public static string MonacoDirectory
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(_monacoDirectory))
|
||||
{
|
||||
_monacoDirectory = GetRuntimeMonacoDirectory();
|
||||
}
|
||||
|
||||
return Directory.Exists(monacoPath) ?
|
||||
monacoPath :
|
||||
throw new DirectoryNotFoundException($"Monaco assets directory not found at {monacoPath}");
|
||||
return _monacoDirectory;
|
||||
}
|
||||
}
|
||||
|
||||
public static JsonDocument GetLanguages()
|
||||
|
||||
@@ -7,7 +7,6 @@ using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using PowerToys.Interop;
|
||||
|
||||
@@ -53,18 +52,16 @@ namespace ManagedCommon
|
||||
Trace.AutoFlush = true;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void LogError(string message)
|
||||
{
|
||||
Log(message, Error);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void LogError(string message, Exception ex)
|
||||
{
|
||||
if (ex == null)
|
||||
{
|
||||
Log(message, Error);
|
||||
LogError(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -87,31 +84,26 @@ namespace ManagedCommon
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void LogWarning(string message)
|
||||
{
|
||||
Log(message, Warning);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void LogInfo(string message)
|
||||
{
|
||||
Log(message, Info);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void LogDebug(string message)
|
||||
{
|
||||
Log(message, Debug);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void LogTrace()
|
||||
{
|
||||
Log(string.Empty, TraceFlag);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void Log(string message, string type)
|
||||
{
|
||||
Trace.WriteLine("[" + DateTime.Now.TimeOfDay + "] [" + type + "] " + GetCallerInfo());
|
||||
@@ -124,49 +116,13 @@ namespace ManagedCommon
|
||||
Trace.Unindent();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static string GetCallerInfo()
|
||||
{
|
||||
StackTrace stackTrace = new();
|
||||
|
||||
var callerMethod = GetCallerMethod(stackTrace);
|
||||
|
||||
return $"{callerMethod?.DeclaringType?.Name}::{callerMethod.Name}";
|
||||
}
|
||||
|
||||
private static MethodBase GetCallerMethod(StackTrace stackTrace)
|
||||
{
|
||||
const int topFrame = 3;
|
||||
|
||||
var topMethod = stackTrace.GetFrame(topFrame)?.GetMethod();
|
||||
|
||||
try
|
||||
{
|
||||
if (topMethod?.Name == nameof(IAsyncStateMachine.MoveNext) && typeof(IAsyncStateMachine).IsAssignableFrom(topMethod?.DeclaringType))
|
||||
{
|
||||
// Async method; return actual method as determined by heuristic:
|
||||
// "Nearest method on stack to async state-machine's MoveNext() in same namespace but in a different type".
|
||||
// There are tighter ways of determining the actual method, but this is good enough and probably faster.
|
||||
for (int deepFrame = topFrame + 1; deepFrame < stackTrace.FrameCount; deepFrame++)
|
||||
{
|
||||
var deepMethod = stackTrace.GetFrame(deepFrame)?.GetMethod();
|
||||
|
||||
if (deepMethod?.DeclaringType != topMethod?.DeclaringType && deepMethod?.DeclaringType?.Namespace == topMethod?.DeclaringType?.Namespace)
|
||||
{
|
||||
return deepMethod;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Ignore exceptions in Release. The code above won't throw, but if it does, we don't want to crash the app.
|
||||
#if DEBUG
|
||||
throw;
|
||||
#endif
|
||||
}
|
||||
|
||||
return topMethod;
|
||||
var methodName = stackTrace.GetFrame(3)?.GetMethod();
|
||||
var className = methodName?.DeclaringType.Name;
|
||||
return className + "::" + methodName?.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<PropertyGroup Label="Globals">
|
||||
@@ -141,7 +141,7 @@
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2739.15\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2739.15\build\native\Microsoft.Web.WebView2.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
@@ -153,7 +153,7 @@
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2739.15\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2739.15\build\native\Microsoft.Web.WebView2.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -4,5 +4,5 @@
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.SDK.BuildTools" version="10.0.22621.2428" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK" version="1.6.241114003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK" version="1.6.240923002" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -2,9 +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.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using MouseJump.Common.Helpers;
|
||||
using MouseJump.Common.Imaging;
|
||||
@@ -17,7 +16,7 @@ namespace MouseJump.Common.UnitTests.Helpers;
|
||||
public static class DrawingHelperTests
|
||||
{
|
||||
[TestClass]
|
||||
public sealed class RenderPreviewTests
|
||||
public sealed class GetPreviewLayoutTests
|
||||
{
|
||||
public sealed class TestCase
|
||||
{
|
||||
@@ -47,7 +46,7 @@ public static class DrawingHelperTests
|
||||
yield return new object[]
|
||||
{
|
||||
new TestCase(
|
||||
previewStyle: StyleHelper.BezelledPreviewStyle,
|
||||
previewStyle: StyleHelper.DefaultPreviewStyle,
|
||||
screens: new List<RectangleInfo>()
|
||||
{
|
||||
new(0, 0, 500, 500),
|
||||
@@ -63,7 +62,7 @@ public static class DrawingHelperTests
|
||||
yield return new object[]
|
||||
{
|
||||
new TestCase(
|
||||
previewStyle: StyleHelper.BezelledPreviewStyle,
|
||||
previewStyle: StyleHelper.DefaultPreviewStyle,
|
||||
screens: new List<RectangleInfo>()
|
||||
{
|
||||
new(5120, 349, 1920, 1080),
|
||||
@@ -80,7 +79,7 @@ public static class DrawingHelperTests
|
||||
public void RunTestCases(TestCase data)
|
||||
{
|
||||
// load the fake desktop image
|
||||
using var desktopImage = RenderPreviewTests.LoadImageResource(data.DesktopImageFilename);
|
||||
using var desktopImage = GetPreviewLayoutTests.LoadImageResource(data.DesktopImageFilename);
|
||||
|
||||
// draw the preview image
|
||||
var previewLayout = LayoutHelper.GetPreviewLayout(
|
||||
@@ -91,29 +90,28 @@ public static class DrawingHelperTests
|
||||
using var actual = DrawingHelper.RenderPreview(previewLayout, imageCopyService);
|
||||
|
||||
// load the expected image
|
||||
var expected = RenderPreviewTests.LoadImageResource(data.ExpectedImageFilename);
|
||||
var expected = GetPreviewLayoutTests.LoadImageResource(data.ExpectedImageFilename);
|
||||
|
||||
// compare the images
|
||||
var screens = System.Windows.Forms.Screen.AllScreens;
|
||||
AssertImagesEqual(expected, actual);
|
||||
}
|
||||
|
||||
private static Bitmap LoadImageResource(string filename)
|
||||
{
|
||||
// assume embedded resources are in the same source folder as this
|
||||
// class, and the namespace hierarchy matches the folder structure.
|
||||
// that way we can build resource names from the current namespace
|
||||
var resourcePrefix = typeof(DrawingHelperTests).Namespace;
|
||||
var resourceName = $"{resourcePrefix}.{filename}";
|
||||
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
var assemblyName = new AssemblyName(assembly.FullName ?? throw new InvalidOperationException());
|
||||
var resourceName = $"{typeof(DrawingHelperTests).Namespace}.{filename.Replace("/", ".")}";
|
||||
var resourceNames = assembly.GetManifestResourceNames();
|
||||
if (!resourceNames.Contains(resourceName))
|
||||
{
|
||||
var message = new StringBuilder();
|
||||
message.AppendLine(CultureInfo.InvariantCulture, $"Embedded resource '{resourceName}' does not exist.");
|
||||
message.AppendLine($"Known resources:");
|
||||
foreach (var name in resourceNames)
|
||||
{
|
||||
message.AppendLine(name);
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(message.ToString());
|
||||
var message = $"Embedded resource '{resourceName}' does not exist. " +
|
||||
"Valid resource names are: \r\n" + string.Join("\r\n", resourceNames);
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
var stream = assembly.GetManifestResourceStream(resourceName)
|
||||
@@ -123,7 +121,7 @@ public static class DrawingHelperTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Naive / brute force image comparison - we can optimize this later :-)
|
||||
/// Naive / brute force image comparison - we can optimise this later :-)
|
||||
/// </summary>
|
||||
private static void AssertImagesEqual(Bitmap expected, Bitmap actual)
|
||||
{
|
||||
|
||||
@@ -129,7 +129,7 @@ public static class LayoutHelperTests
|
||||
public static IEnumerable<object[]> GetTestCases()
|
||||
{
|
||||
// happy path - single screen with 50% scaling,
|
||||
// *has* a preview border but *no* screenshot borders
|
||||
// *has* a preview borders but *no* screenshot borders
|
||||
//
|
||||
// +----------------+
|
||||
// | |
|
||||
@@ -160,7 +160,7 @@ public static class LayoutHelperTests
|
||||
new(0, 0, 1024, 768),
|
||||
};
|
||||
var activatedLocation = new PointInfo(512, 384);
|
||||
var expectedResult = new PreviewLayout(
|
||||
var previewLayout = new PreviewLayout(
|
||||
virtualScreen: new(0, 0, 1024, 768),
|
||||
screens: screens,
|
||||
activatedScreenIndex: 0,
|
||||
@@ -183,7 +183,7 @@ public static class LayoutHelperTests
|
||||
contentBounds: new(6, 6, 512, 384)
|
||||
),
|
||||
});
|
||||
yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, expectedResult) };
|
||||
yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, previewLayout) };
|
||||
|
||||
// happy path - single screen with 50% scaling,
|
||||
// *no* preview borders but *has* screenshot borders
|
||||
@@ -217,7 +217,7 @@ public static class LayoutHelperTests
|
||||
new(0, 0, 1024, 768),
|
||||
};
|
||||
activatedLocation = new PointInfo(512, 384);
|
||||
expectedResult = new PreviewLayout(
|
||||
previewLayout = new PreviewLayout(
|
||||
virtualScreen: new(0, 0, 1024, 768),
|
||||
screens: screens,
|
||||
activatedScreenIndex: 0,
|
||||
@@ -240,59 +240,7 @@ public static class LayoutHelperTests
|
||||
contentBounds: new(6, 6, 500, 372)
|
||||
),
|
||||
});
|
||||
yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, expectedResult) };
|
||||
|
||||
// rounding error check - single screen with 33% scaling,
|
||||
// no borders, check to make sure form scales to exactly
|
||||
// fill the canvas size with no rounding errors.
|
||||
//
|
||||
// in this test the preview width is 300 and the desktop is
|
||||
// 900, so the scaling factor is 1/3, but this gets rounded
|
||||
// to 0.3333333333333333333333333333, and 900 times this value
|
||||
// is 299.99999999999999999999999997. if we don't scale correctly
|
||||
// the resulting form width might only be 299 pixels instead of 300
|
||||
//
|
||||
// +----------------+
|
||||
// | |
|
||||
// | 0 |
|
||||
// | |
|
||||
// +----------------+
|
||||
previewStyle = new PreviewStyle(
|
||||
canvasSize: new(
|
||||
width: 300,
|
||||
height: 200
|
||||
),
|
||||
canvasStyle: BoxStyle.Empty,
|
||||
screenStyle: BoxStyle.Empty);
|
||||
screens = new List<RectangleInfo>
|
||||
{
|
||||
new(0, 0, 900, 200),
|
||||
};
|
||||
activatedLocation = new PointInfo(450, 100);
|
||||
expectedResult = new PreviewLayout(
|
||||
virtualScreen: new(0, 0, 900, 200),
|
||||
screens: screens,
|
||||
activatedScreenIndex: 0,
|
||||
formBounds: new(300, 66.5m, 300, 67),
|
||||
previewStyle: previewStyle,
|
||||
previewBounds: new(
|
||||
outerBounds: new(0, 0, 300, 67),
|
||||
marginBounds: new(0, 0, 300, 67),
|
||||
borderBounds: new(0, 0, 300, 67),
|
||||
paddingBounds: new(0, 0, 300, 67),
|
||||
contentBounds: new(0, 0, 300, 67)
|
||||
),
|
||||
screenshotBounds: new()
|
||||
{
|
||||
new(
|
||||
outerBounds: new(0, 0, 300, 67),
|
||||
marginBounds: new(0, 0, 300, 67),
|
||||
borderBounds: new(0, 0, 300, 67),
|
||||
paddingBounds: new(0, 0, 300, 67),
|
||||
contentBounds: new(0, 0, 300, 67)
|
||||
),
|
||||
});
|
||||
yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, expectedResult) };
|
||||
yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, previewLayout) };
|
||||
|
||||
// primary monitor not topmost / leftmost - if there are screens
|
||||
// that are further left or higher up than the primary monitor
|
||||
@@ -343,7 +291,7 @@ public static class LayoutHelperTests
|
||||
new(0, 0, 5120, 1440),
|
||||
};
|
||||
activatedLocation = new(-960, 60);
|
||||
expectedResult = new PreviewLayout(
|
||||
previewLayout = new PreviewLayout(
|
||||
virtualScreen: new(-1920, -480, 7040, 1920),
|
||||
screens: screens,
|
||||
activatedScreenIndex: 0,
|
||||
@@ -373,7 +321,7 @@ public static class LayoutHelperTests
|
||||
contentBounds: new(204, 60, 500, 132)
|
||||
),
|
||||
});
|
||||
yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, expectedResult) };
|
||||
yield return new object[] { new TestCase(previewStyle, screens, activatedLocation, previewLayout) };
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 202 KiB After Width: | Height: | Size: 215 KiB |
@@ -15,49 +15,45 @@ public static class SizeInfoTests
|
||||
{
|
||||
public sealed class TestCase
|
||||
{
|
||||
public TestCase(SizeInfo source, SizeInfo bounds, SizeInfo expectedResult, decimal scalingRatio)
|
||||
public TestCase(SizeInfo obj, SizeInfo bounds, SizeInfo expectedResult)
|
||||
{
|
||||
this.Source = source;
|
||||
this.Obj = obj;
|
||||
this.Bounds = bounds;
|
||||
this.ExpectedResult = expectedResult;
|
||||
this.ScalingRatio = scalingRatio;
|
||||
}
|
||||
|
||||
public SizeInfo Source { get; }
|
||||
public SizeInfo Obj { get; }
|
||||
|
||||
public SizeInfo Bounds { get; }
|
||||
|
||||
public SizeInfo ExpectedResult { get; }
|
||||
|
||||
public decimal ScalingRatio { get; }
|
||||
}
|
||||
|
||||
public static IEnumerable<object[]> GetTestCases()
|
||||
{
|
||||
// identity tests
|
||||
yield return new object[] { new TestCase(new(512, 384), new(512, 384), new(512, 384), 1), };
|
||||
yield return new object[] { new TestCase(new(1024, 768), new(1024, 768), new(1024, 768), 1), };
|
||||
yield return new object[] { new TestCase(new(512, 384), new(512, 384), new(512, 384)), };
|
||||
yield return new object[] { new TestCase(new(1024, 768), new(1024, 768), new(1024, 768)), };
|
||||
|
||||
// general tests
|
||||
yield return new object[] { new TestCase(new(512, 384), new(2048, 1536), new(2048, 1536), 4), };
|
||||
yield return new object[] { new TestCase(new(2048, 1536), new(1024, 768), new(1024, 768), 0.5m), };
|
||||
yield return new object[] { new TestCase(new(512, 384), new(2048, 1536), new(2048, 1536)), };
|
||||
yield return new object[] { new TestCase(new(2048, 1536), new(1024, 768), new(1024, 768)), };
|
||||
|
||||
// scale to fit width
|
||||
yield return new object[] { new TestCase(new(512, 384), new(2048, 3072), new(2048, 1536), 4), };
|
||||
yield return new object[] { new TestCase(new(512, 384), new(2048, 3072), new(2048, 1536)), };
|
||||
|
||||
// scale to fit height
|
||||
yield return new object[] { new TestCase(new(512, 384), new(4096, 1536), new(2048, 1536), 4), };
|
||||
yield return new object[] { new TestCase(new(512, 384), new(4096, 1536), new(2048, 1536)), };
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DynamicData(nameof(GetTestCases), DynamicDataSourceType.Method)]
|
||||
public void RunTestCases(TestCase data)
|
||||
{
|
||||
var actual = data.Source.ScaleToFit(data.Bounds, out var scalingRatio);
|
||||
var actual = data.Obj.ScaleToFit(data.Bounds);
|
||||
var expected = data.ExpectedResult;
|
||||
Assert.AreEqual(expected.Width, actual.Width);
|
||||
Assert.AreEqual(expected.Height, actual.Height);
|
||||
Assert.AreEqual(scalingRatio, data.ScalingRatio);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Globalization;
|
||||
|
||||
namespace MouseJump.Common.Helpers;
|
||||
|
||||
public static class ConfigHelper
|
||||
{
|
||||
public static Color? ToUnnamedColor(Color? value)
|
||||
{
|
||||
if (!value.HasValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var color = value.Value;
|
||||
return Color.FromArgb(color.A, color.R, color.G, color.B);
|
||||
}
|
||||
|
||||
public static string? SerializeToConfigColorString(Color? value)
|
||||
{
|
||||
if (!value.HasValue)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var color = value.Value;
|
||||
return color switch
|
||||
{
|
||||
Color { IsNamedColor: true } =>
|
||||
$"{nameof(Color)}.{color.Name}",
|
||||
Color { IsSystemColor: true } =>
|
||||
$"{nameof(SystemColors)}.{color.Name}",
|
||||
_ =>
|
||||
$"#{color.R:X2}{color.G:X2}{color.B:X2}",
|
||||
};
|
||||
}
|
||||
|
||||
public static Color? DeserializeFromConfigColorString(string? value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// e.g. "#AABBCC"
|
||||
if (value.StartsWith('#'))
|
||||
{
|
||||
var culture = CultureInfo.InvariantCulture;
|
||||
if ((value.Length == 7)
|
||||
&& int.TryParse(value[1..3], NumberStyles.HexNumber, culture, out var r)
|
||||
&& int.TryParse(value[3..5], NumberStyles.HexNumber, culture, out var g)
|
||||
&& int.TryParse(value[5..7], NumberStyles.HexNumber, culture, out var b))
|
||||
{
|
||||
return Color.FromArgb(0xFF, r, g, b);
|
||||
}
|
||||
}
|
||||
|
||||
const StringComparison comparison = StringComparison.InvariantCulture;
|
||||
|
||||
// e.g. "Color.Red"
|
||||
const string colorPrefix = $"{nameof(Color)}.";
|
||||
if (value.StartsWith(colorPrefix, comparison))
|
||||
{
|
||||
var colorName = value[colorPrefix.Length..];
|
||||
var property = typeof(Color).GetProperties()
|
||||
.SingleOrDefault(property => property.Name == colorName);
|
||||
if (property is not null)
|
||||
{
|
||||
return (Color?)property.GetValue(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
// e.g. "SystemColors.Highlight"
|
||||
const string systemColorPrefix = $"{nameof(SystemColors)}.";
|
||||
if (value.StartsWith(systemColorPrefix, comparison))
|
||||
{
|
||||
var colorName = value[systemColorPrefix.Length..];
|
||||
var property = typeof(SystemColors).GetProperties()
|
||||
.SingleOrDefault(property => property.Name == colorName);
|
||||
if (property is not null)
|
||||
{
|
||||
return (Color?)property.GetValue(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -102,13 +102,8 @@ public static class DrawingHelper
|
||||
return;
|
||||
}
|
||||
|
||||
if (borderStyle.Color is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// draw the main box border
|
||||
using var borderBrush = new SolidBrush(borderStyle.Color.Value);
|
||||
using var borderBrush = new SolidBrush(borderStyle.Color);
|
||||
var borderRegion = new Region(boxBounds.BorderBounds.ToRectangle());
|
||||
borderRegion.Exclude(boxBounds.PaddingBounds.ToRectangle());
|
||||
graphics.FillRegion(borderBrush, borderRegion);
|
||||
|
||||
@@ -46,13 +46,16 @@ public static class LayoutHelper
|
||||
.Shrink(previewStyle.CanvasStyle.BorderStyle)
|
||||
.Shrink(previewStyle.CanvasStyle.PaddingStyle);
|
||||
|
||||
// scale the virtual screen to fit inside the content area
|
||||
var screenScalingRatio = builder.VirtualScreen.Size
|
||||
.ScaleToFitRatio(maxContentSize);
|
||||
|
||||
// work out the actual size of the "content area" by scaling the virtual screen
|
||||
// to fit inside the maximum content area while maintaining its aspect ration.
|
||||
// we'll also offset it to allow for any margins, borders and padding
|
||||
var contentBounds = builder.VirtualScreen.Size
|
||||
.ScaleToFit(maxContentSize, out var scalingRatio)
|
||||
.Round()
|
||||
.Clamp(maxContentSize)
|
||||
.Scale(screenScalingRatio)
|
||||
.Floor()
|
||||
.PlaceAt(0, 0)
|
||||
.Offset(previewStyle.CanvasStyle.MarginStyle.Left, previewStyle.CanvasStyle.MarginStyle.Top)
|
||||
.Offset(previewStyle.CanvasStyle.BorderStyle.Left, previewStyle.CanvasStyle.BorderStyle.Top)
|
||||
@@ -79,16 +82,16 @@ public static class LayoutHelper
|
||||
screen => LayoutHelper.GetBoxBoundsFromOuterBounds(
|
||||
screen
|
||||
.Offset(builder.VirtualScreen.Location.ToSize().Invert())
|
||||
.Scale(scalingRatio)
|
||||
.Scale(screenScalingRatio)
|
||||
.Offset(builder.PreviewBounds.ContentBounds.Location.ToSize())
|
||||
.Round(),
|
||||
.Truncate(),
|
||||
previewStyle.ScreenStyle))
|
||||
.ToList();
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
public static RectangleInfo GetCombinedScreenBounds(List<RectangleInfo> screens)
|
||||
internal static RectangleInfo GetCombinedScreenBounds(List<RectangleInfo> screens)
|
||||
{
|
||||
return screens.Skip(1).Aggregate(
|
||||
seed: screens.First(),
|
||||
|
||||
@@ -102,7 +102,7 @@ public static class MouseHelper
|
||||
/// See https://github.com/microsoft/PowerToys/issues/24523
|
||||
/// https://github.com/microsoft/PowerToys/pull/24527
|
||||
/// </remarks>
|
||||
private static void SimulateMouseMovementEvent(PointInfo location)
|
||||
internal static void SimulateMouseMovementEvent(PointInfo location)
|
||||
{
|
||||
var inputs = new User32.INPUT[]
|
||||
{
|
||||
|
||||
@@ -10,49 +10,9 @@ namespace MouseJump.Common.Helpers;
|
||||
public static class StyleHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Compact (legacy) preview style
|
||||
/// Default v2 preview style
|
||||
/// </summary>
|
||||
public static readonly PreviewStyle CompactPreviewStyle = new(
|
||||
canvasSize: new(
|
||||
width: 1600,
|
||||
height: 1200
|
||||
),
|
||||
canvasStyle: new(
|
||||
marginStyle: MarginStyle.Empty,
|
||||
borderStyle: new(
|
||||
color: SystemColors.Highlight,
|
||||
all: 6,
|
||||
depth: 0
|
||||
),
|
||||
paddingStyle: new(
|
||||
all: 0
|
||||
),
|
||||
backgroundStyle: new(
|
||||
color1: Color.FromArgb(0xFF, 0x0D, 0x57, 0xD2),
|
||||
color2: Color.FromArgb(0xFF, 0x03, 0x44, 0xC0)
|
||||
)
|
||||
),
|
||||
screenStyle: new(
|
||||
marginStyle: new(
|
||||
all: 0
|
||||
),
|
||||
borderStyle: new(
|
||||
color: Color.FromArgb(0xFF, 0x22, 0x22, 0x22),
|
||||
all: 0,
|
||||
depth: 0
|
||||
),
|
||||
paddingStyle: PaddingStyle.Empty,
|
||||
backgroundStyle: new(
|
||||
color1: Color.MidnightBlue,
|
||||
color2: Color.MidnightBlue
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Bezelled preview style
|
||||
/// </summary>
|
||||
public static readonly PreviewStyle BezelledPreviewStyle = new(
|
||||
public static readonly PreviewStyle DefaultPreviewStyle = new(
|
||||
canvasSize: new(
|
||||
width: 1600,
|
||||
height: 1200
|
||||
@@ -89,6 +49,46 @@ public static class StyleHelper
|
||||
)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Legacy preview style
|
||||
/// </summary>
|
||||
public static readonly PreviewStyle LegacyPreviewStyle = new(
|
||||
canvasSize: new(
|
||||
width: 1600,
|
||||
height: 1200
|
||||
),
|
||||
canvasStyle: new(
|
||||
marginStyle: MarginStyle.Empty,
|
||||
borderStyle: new(
|
||||
color: SystemColors.Highlight,
|
||||
all: 6,
|
||||
depth: 0
|
||||
),
|
||||
paddingStyle: new(
|
||||
all: 0
|
||||
),
|
||||
backgroundStyle: new(
|
||||
color1: Color.FromArgb(0xFF, 0x0D, 0x57, 0xD2),
|
||||
color2: Color.FromArgb(0xFF, 0x03, 0x44, 0xC0)
|
||||
)
|
||||
),
|
||||
screenStyle: new(
|
||||
marginStyle: new(
|
||||
all: 0
|
||||
),
|
||||
borderStyle: new(
|
||||
color: Color.FromArgb(0xFF, 0x22, 0x22, 0x22),
|
||||
all: 0,
|
||||
depth: 0
|
||||
),
|
||||
paddingStyle: PaddingStyle.Empty,
|
||||
backgroundStyle: new(
|
||||
color1: Color.MidnightBlue,
|
||||
color2: Color.MidnightBlue
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
public static PreviewStyle WithCanvasSize(this PreviewStyle previewStyle, SizeInfo canvasSize)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(previewStyle);
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Drawing.Drawing2D;
|
||||
|
||||
using MouseJump.Common.Models.Drawing;
|
||||
|
||||
namespace MouseJump.Common.Imaging;
|
||||
@@ -33,11 +31,6 @@ public sealed class StaticImageRegionCopyService : IImageRegionCopyService
|
||||
RectangleInfo sourceBounds,
|
||||
RectangleInfo targetBounds)
|
||||
{
|
||||
// prevent the background bleeding through into screen images
|
||||
// (see https://github.com/mikeclayton/FancyMouse/issues/44)
|
||||
targetGraphics.PixelOffsetMode = PixelOffsetMode.Half;
|
||||
targetGraphics.InterpolationMode = InterpolationMode.NearestNeighbor;
|
||||
|
||||
targetGraphics.DrawImage(
|
||||
image: this.SourceImage,
|
||||
destRect: targetBounds.ToRectangle(),
|
||||
|
||||
@@ -203,15 +203,6 @@ public sealed class RectangleInfo
|
||||
public RectangleInfo Offset(decimal dx, decimal dy) =>
|
||||
new(this.X + dx, this.Y + dy, this.Width, this.Height);
|
||||
|
||||
public RectangleInfo Round() =>
|
||||
this.Round(0);
|
||||
|
||||
public RectangleInfo Round(int decimals) => new(
|
||||
Math.Round(this.X, decimals),
|
||||
Math.Round(this.Y, decimals),
|
||||
Math.Round(this.Width, decimals),
|
||||
Math.Round(this.Height, decimals));
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new <see cref="RectangleInfo"/> that is a scaled version of the current rectangle.
|
||||
/// The dimensions of the new rectangle are calculated by multiplying the current rectangle's dimensions by the scaling factor.
|
||||
|
||||
@@ -12,8 +12,6 @@ public sealed class ScreenInfo
|
||||
{
|
||||
public ScreenInfo(int handle, bool primary, RectangleInfo displayArea, RectangleInfo workingArea)
|
||||
{
|
||||
// this.Handle is a HMONITOR that has been cast to an int because we don't want
|
||||
// to expose the HMONITOR type outside the current assembly.
|
||||
this.Handle = handle;
|
||||
this.Primary = primary;
|
||||
this.DisplayArea = displayArea ?? throw new ArgumentNullException(nameof(displayArea));
|
||||
|
||||
@@ -33,20 +33,6 @@ public sealed class SizeInfo
|
||||
get;
|
||||
}
|
||||
|
||||
public SizeInfo Clamp(SizeInfo max)
|
||||
{
|
||||
return new(
|
||||
width: Math.Clamp(this.Width, 0, max.Width),
|
||||
height: Math.Clamp(this.Height, 0, max.Height));
|
||||
}
|
||||
|
||||
public SizeInfo Clamp(decimal maxWidth, decimal maxHeight)
|
||||
{
|
||||
return new(
|
||||
width: Math.Clamp(this.Width, 0, maxWidth),
|
||||
height: Math.Clamp(this.Height, 0, maxHeight));
|
||||
}
|
||||
|
||||
public SizeInfo Enlarge(BorderStyle border) =>
|
||||
new(
|
||||
this.Width + border.Horizontal,
|
||||
@@ -57,17 +43,6 @@ public sealed class SizeInfo
|
||||
this.Width + padding.Horizontal,
|
||||
this.Height + padding.Vertical);
|
||||
|
||||
/// <summary>
|
||||
/// Rounds down the width and height of this size to the nearest whole number.
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="SizeInfo"/> instance with floored dimensions.</returns>
|
||||
public SizeInfo Floor()
|
||||
{
|
||||
return new SizeInfo(
|
||||
Math.Floor(this.Width),
|
||||
Math.Floor(this.Height));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the intersection of this size with another size, resulting in a size that represents
|
||||
/// the overlapping dimensions. Both sizes must be non-negative.
|
||||
@@ -94,6 +69,19 @@ public sealed class SizeInfo
|
||||
public SizeInfo Invert() =>
|
||||
new(-this.Width, -this.Height);
|
||||
|
||||
public SizeInfo Scale(decimal scalingFactor) => new(
|
||||
this.Width * scalingFactor,
|
||||
this.Height * scalingFactor);
|
||||
|
||||
public SizeInfo Shrink(BorderStyle border) =>
|
||||
new(this.Width - border.Horizontal, this.Height - border.Vertical);
|
||||
|
||||
public SizeInfo Shrink(MarginStyle margin) =>
|
||||
new(this.Width - margin.Horizontal, this.Height - margin.Vertical);
|
||||
|
||||
public SizeInfo Shrink(PaddingStyle padding) =>
|
||||
new(this.Width - padding.Horizontal, this.Height - padding.Vertical);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="RectangleInfo"/> instance representing a rectangle with this size,
|
||||
/// positioned at the specified coordinates.
|
||||
@@ -104,39 +92,32 @@ public sealed class SizeInfo
|
||||
public RectangleInfo PlaceAt(decimal x, decimal y) =>
|
||||
new(x, y, this.Width, this.Height);
|
||||
|
||||
public SizeInfo Round() =>
|
||||
this.Round(0);
|
||||
|
||||
public SizeInfo Round(int decimals) => new(
|
||||
Math.Round(this.Width, decimals),
|
||||
Math.Round(this.Height, decimals));
|
||||
|
||||
public SizeInfo Scale(decimal scalingFactor) => new(
|
||||
this.Width * scalingFactor,
|
||||
this.Height * scalingFactor);
|
||||
|
||||
/// <summary>
|
||||
/// Scales this size to fit within the bounds of another size, while maintaining the aspect ratio.
|
||||
/// </summary>
|
||||
/// <param name="bounds">The size to fit this size into.</param>
|
||||
/// <returns>A new <see cref="SizeInfo"/> instance representing the scaled size.</returns>
|
||||
public SizeInfo ScaleToFit(SizeInfo bounds, out decimal scalingRatio)
|
||||
public SizeInfo ScaleToFit(SizeInfo bounds)
|
||||
{
|
||||
var widthRatio = bounds.Width / this.Width;
|
||||
var heightRatio = bounds.Height / this.Height;
|
||||
switch (widthRatio.CompareTo(heightRatio))
|
||||
return widthRatio.CompareTo(heightRatio) switch
|
||||
{
|
||||
case < 0:
|
||||
scalingRatio = widthRatio;
|
||||
return new(bounds.Width, this.Height * widthRatio);
|
||||
case 0:
|
||||
// widthRatio and heightRatio are the same, so just pick one
|
||||
scalingRatio = widthRatio;
|
||||
return bounds;
|
||||
case > 0:
|
||||
scalingRatio = heightRatio;
|
||||
return new(this.Width * heightRatio, bounds.Height);
|
||||
}
|
||||
< 0 => new(bounds.Width, this.Height * widthRatio),
|
||||
0 => bounds,
|
||||
> 0 => new(this.Width * heightRatio, bounds.Height),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rounds down the width and height of this size to the nearest whole number.
|
||||
/// </summary>
|
||||
/// <returns>A new <see cref="SizeInfo"/> instance with floored dimensions.</returns>
|
||||
public SizeInfo Floor()
|
||||
{
|
||||
return new SizeInfo(
|
||||
Math.Floor(this.Width),
|
||||
Math.Floor(this.Height));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -159,15 +140,6 @@ public sealed class SizeInfo
|
||||
return scalingRatio;
|
||||
}
|
||||
|
||||
public SizeInfo Shrink(BorderStyle border) =>
|
||||
new(this.Width - border.Horizontal, this.Height - border.Vertical);
|
||||
|
||||
public SizeInfo Shrink(MarginStyle margin) =>
|
||||
new(this.Width - margin.Horizontal, this.Height - margin.Vertical);
|
||||
|
||||
public SizeInfo Shrink(PaddingStyle padding) =>
|
||||
new(this.Width - padding.Horizontal, this.Height - padding.Vertical);
|
||||
|
||||
public Size ToSize() => new((int)this.Width, (int)this.Height);
|
||||
|
||||
public Point ToPoint() => new((int)this.Width, (int)this.Height);
|
||||
|
||||
@@ -1,12 +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.
|
||||
|
||||
namespace MouseJump.Common.Models.Settings;
|
||||
|
||||
public enum PreviewType
|
||||
{
|
||||
Custom = 0,
|
||||
Compact = 1,
|
||||
Bezelled = 2,
|
||||
}
|
||||
@@ -9,14 +9,14 @@ namespace MouseJump.Common.Models.Styles;
|
||||
/// </summary>
|
||||
public sealed class BorderStyle
|
||||
{
|
||||
public static readonly BorderStyle Empty = new(null, 0, 0);
|
||||
public static readonly BorderStyle Empty = new(Color.Transparent, 0, 0);
|
||||
|
||||
public BorderStyle(Color? color, decimal all, decimal depth)
|
||||
public BorderStyle(Color color, decimal all, decimal depth)
|
||||
: this(color, all, all, all, all, depth)
|
||||
{
|
||||
}
|
||||
|
||||
public BorderStyle(Color? color, decimal left, decimal top, decimal right, decimal bottom, decimal depth)
|
||||
public BorderStyle(Color color, decimal left, decimal top, decimal right, decimal bottom, decimal depth)
|
||||
{
|
||||
this.Color = color;
|
||||
this.Left = left;
|
||||
@@ -26,7 +26,7 @@ public sealed class BorderStyle
|
||||
this.Depth = depth;
|
||||
}
|
||||
|
||||
public Color? Color
|
||||
public Color Color
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
@@ -10,10 +10,6 @@ using System.Threading;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
|
||||
using MouseJump.Common.Helpers;
|
||||
using MouseJump.Common.Models.Drawing;
|
||||
using MouseJump.Common.Models.Settings;
|
||||
using MouseJump.Common.Models.Styles;
|
||||
|
||||
namespace MouseJumpUI.Helpers;
|
||||
|
||||
@@ -97,65 +93,4 @@ internal sealed class SettingsHelper
|
||||
{
|
||||
this.CurrentSettings = this.LoadSettings();
|
||||
}
|
||||
|
||||
public static PreviewStyle GetActivePreviewStyle(MouseJumpSettings settings)
|
||||
{
|
||||
var previewType = Enum.TryParse<PreviewType>(settings.Properties.PreviewType, true, out var previewTypeResult)
|
||||
? previewTypeResult
|
||||
: PreviewType.Bezelled;
|
||||
|
||||
var canvasSize = new SizeInfo(
|
||||
settings.Properties.ThumbnailSize.Width,
|
||||
settings.Properties.ThumbnailSize.Height);
|
||||
|
||||
var properties = settings.Properties;
|
||||
|
||||
var previewStyle = previewType switch
|
||||
{
|
||||
PreviewType.Compact => StyleHelper.CompactPreviewStyle.WithCanvasSize(canvasSize),
|
||||
PreviewType.Bezelled => StyleHelper.BezelledPreviewStyle.WithCanvasSize(canvasSize),
|
||||
PreviewType.Custom => new PreviewStyle(
|
||||
canvasSize: canvasSize,
|
||||
canvasStyle: new(
|
||||
marginStyle: new(0),
|
||||
borderStyle: new(
|
||||
color: ConfigHelper.DeserializeFromConfigColorString(
|
||||
properties.BorderColor),
|
||||
all: properties.BorderThickness,
|
||||
depth: properties.Border3dDepth
|
||||
),
|
||||
paddingStyle: new(
|
||||
all: properties.BorderPadding
|
||||
),
|
||||
backgroundStyle: new(
|
||||
color1: ConfigHelper.DeserializeFromConfigColorString(
|
||||
properties.BackgroundColor1),
|
||||
color2: ConfigHelper.DeserializeFromConfigColorString(
|
||||
properties.BackgroundColor2)
|
||||
)
|
||||
),
|
||||
screenStyle: new(
|
||||
marginStyle: new(
|
||||
all: properties.ScreenMargin
|
||||
),
|
||||
borderStyle: new(
|
||||
color: ConfigHelper.DeserializeFromConfigColorString(
|
||||
properties.BezelColor),
|
||||
all: properties.BezelThickness,
|
||||
depth: properties.Bezel3dDepth
|
||||
),
|
||||
paddingStyle: new(0),
|
||||
backgroundStyle: new(
|
||||
color1: ConfigHelper.DeserializeFromConfigColorString(
|
||||
properties.ScreenColor1),
|
||||
color2: ConfigHelper.DeserializeFromConfigColorString(
|
||||
properties.ScreenColor2)
|
||||
)
|
||||
)),
|
||||
_ => throw new InvalidOperationException(
|
||||
$"Unhandled {nameof(PreviewType)} '{previewType}'"),
|
||||
};
|
||||
|
||||
return previewStyle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,9 +183,12 @@ internal sealed partial class MainForm : Form
|
||||
var appSettings = this.SettingsHelper.CurrentSettings ?? throw new InvalidOperationException();
|
||||
var screens = ScreenHelper.GetAllScreens().Select(screen => screen.DisplayArea).ToList();
|
||||
var activatedLocation = MouseHelper.GetCursorPosition();
|
||||
|
||||
this.PreviewLayout = LayoutHelper.GetPreviewLayout(
|
||||
previewStyle: SettingsHelper.GetActivePreviewStyle(appSettings),
|
||||
previewStyle: StyleHelper.LegacyPreviewStyle.WithCanvasSize(
|
||||
new(
|
||||
appSettings.Properties.ThumbnailSize.Width,
|
||||
appSettings.Properties.ThumbnailSize.Height
|
||||
)),
|
||||
screens: screens,
|
||||
activatedLocation: activatedLocation);
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// 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.Forms;
|
||||
|
||||
namespace MouseJumpUI.Models.Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// Immutable version of a System.Windows.Forms.Padding object with some extra utility methods.
|
||||
/// </summary>
|
||||
public sealed class PaddingInfo
|
||||
{
|
||||
public PaddingInfo(decimal all)
|
||||
: this(all, all, all, all)
|
||||
{
|
||||
}
|
||||
|
||||
public PaddingInfo(decimal left, decimal top, decimal right, decimal bottom)
|
||||
{
|
||||
this.Left = left;
|
||||
this.Top = top;
|
||||
this.Right = right;
|
||||
this.Bottom = bottom;
|
||||
}
|
||||
|
||||
public decimal Left
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public decimal Top
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public decimal Right
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public decimal Bottom
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public decimal Horizontal => this.Left + this.Right;
|
||||
|
||||
public decimal Vertical => this.Top + this.Bottom;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "{" +
|
||||
$"{nameof(this.Left)}={this.Left}," +
|
||||
$"{nameof(this.Top)}={this.Top}," +
|
||||
$"{nameof(this.Right)}={this.Right}," +
|
||||
$"{nameof(this.Bottom)}={this.Bottom}" +
|
||||
"}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Drawing;
|
||||
|
||||
namespace MouseJumpUI.Models.Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// Immutable version of a System.Drawing.Point object with some extra utility methods.
|
||||
/// </summary>
|
||||
public sealed class PointInfo
|
||||
{
|
||||
public PointInfo(decimal x, decimal y)
|
||||
{
|
||||
this.X = x;
|
||||
this.Y = y;
|
||||
}
|
||||
|
||||
public PointInfo(Point point)
|
||||
: this(point.X, point.Y)
|
||||
{
|
||||
}
|
||||
|
||||
public decimal X
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public decimal Y
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public SizeInfo ToSize()
|
||||
{
|
||||
return new((int)this.X, (int)this.Y);
|
||||
}
|
||||
|
||||
public PointInfo Scale(decimal scalingFactor) => new(this.X * scalingFactor, this.Y * scalingFactor);
|
||||
|
||||
public PointInfo Offset(PointInfo amount) => new(this.X + amount.X, this.Y + amount.Y);
|
||||
|
||||
public Point ToPoint() => new((int)this.X, (int)this.Y);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "{" +
|
||||
$"{nameof(this.X)}={this.X}," +
|
||||
$"{nameof(this.Y)}={this.Y}" +
|
||||
"}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
// 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.Drawing;
|
||||
|
||||
namespace MouseJumpUI.Models.Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// Immutable version of a System.Drawing.Rectangle object with some extra utility methods.
|
||||
/// </summary>
|
||||
public sealed class RectangleInfo
|
||||
{
|
||||
public RectangleInfo(decimal x, decimal y, decimal width, decimal height)
|
||||
{
|
||||
this.X = x;
|
||||
this.Y = y;
|
||||
this.Width = width;
|
||||
this.Height = height;
|
||||
}
|
||||
|
||||
public RectangleInfo(Rectangle rectangle)
|
||||
: this(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height)
|
||||
{
|
||||
}
|
||||
|
||||
public RectangleInfo(Point location, SizeInfo size)
|
||||
: this(location.X, location.Y, size.Width, size.Height)
|
||||
{
|
||||
}
|
||||
|
||||
public RectangleInfo(SizeInfo size)
|
||||
: this(0, 0, size.Width, size.Height)
|
||||
{
|
||||
}
|
||||
|
||||
public decimal X
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public decimal Y
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public decimal Width
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public decimal Height
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public decimal Left => this.X;
|
||||
|
||||
public decimal Top => this.Y;
|
||||
|
||||
public decimal Right => this.X + this.Width;
|
||||
|
||||
public decimal Bottom => this.Y + this.Height;
|
||||
|
||||
public SizeInfo Size => new(this.Width, this.Height);
|
||||
|
||||
public PointInfo Location => new(this.X, this.Y);
|
||||
|
||||
public decimal Area => this.Width * this.Height;
|
||||
|
||||
/// <remarks>
|
||||
/// Adapted from https://github.com/dotnet/runtime
|
||||
/// See https://github.com/dotnet/runtime/blob/dfd618dc648ba9b11dd0f8034f78113d69f223cd/src/libraries/System.Drawing.Primitives/src/System/Drawing/Rectangle.cs
|
||||
/// </remarks>
|
||||
public bool Contains(RectangleInfo rect) =>
|
||||
(this.X <= rect.X) && (rect.X + rect.Width <= this.X + this.Width) &&
|
||||
(this.Y <= rect.Y) && (rect.Y + rect.Height <= this.Y + this.Height);
|
||||
|
||||
public RectangleInfo Enlarge(PaddingInfo padding) => new(
|
||||
this.X + padding.Left,
|
||||
this.Y + padding.Top,
|
||||
this.Width + padding.Horizontal,
|
||||
this.Height + padding.Vertical);
|
||||
|
||||
public RectangleInfo Offset(SizeInfo amount) => this.Offset(amount.Width, amount.Height);
|
||||
|
||||
public RectangleInfo Offset(decimal dx, decimal dy) => new(this.X + dx, this.Y + dy, this.Width, this.Height);
|
||||
|
||||
public RectangleInfo Scale(decimal scalingFactor) => new(
|
||||
this.X * scalingFactor,
|
||||
this.Y * scalingFactor,
|
||||
this.Width * scalingFactor,
|
||||
this.Height * scalingFactor);
|
||||
|
||||
public RectangleInfo Center(PointInfo point) => new(
|
||||
x: point.X - (this.Width / 2),
|
||||
y: point.Y - (this.Height / 2),
|
||||
width: this.Width,
|
||||
height: this.Height);
|
||||
|
||||
public PointInfo Midpoint => new(
|
||||
x: this.X + (this.Width / 2),
|
||||
y: this.Y + (this.Height / 2));
|
||||
|
||||
public RectangleInfo Clamp(RectangleInfo outer)
|
||||
{
|
||||
if ((this.Width > outer.Width) || (this.Height > outer.Height))
|
||||
{
|
||||
throw new ArgumentException($"Value cannot be larger than {nameof(outer)}.");
|
||||
}
|
||||
|
||||
return new(
|
||||
x: Math.Clamp(this.X, outer.X, outer.Right - this.Width),
|
||||
y: Math.Clamp(this.Y, outer.Y, outer.Bottom - this.Height),
|
||||
width: this.Width,
|
||||
height: this.Height);
|
||||
}
|
||||
|
||||
public Rectangle ToRectangle() => new((int)this.X, (int)this.Y, (int)this.Width, (int)this.Height);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "{" +
|
||||
$"{nameof(this.Left)}={this.Left}," +
|
||||
$"{nameof(this.Top)}={this.Top}," +
|
||||
$"{nameof(this.Width)}={this.Width}," +
|
||||
$"{nameof(this.Height)}={this.Height}" +
|
||||
"}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
// 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.Drawing;
|
||||
|
||||
namespace MouseJumpUI.Models.Drawing;
|
||||
|
||||
/// <summary>
|
||||
/// Immutable version of a System.Drawing.Size object with some extra utility methods.
|
||||
/// </summary>
|
||||
public sealed class SizeInfo
|
||||
{
|
||||
public SizeInfo(decimal width, decimal height)
|
||||
{
|
||||
this.Width = width;
|
||||
this.Height = height;
|
||||
}
|
||||
|
||||
public SizeInfo(Size size)
|
||||
: this(size.Width, size.Height)
|
||||
{
|
||||
}
|
||||
|
||||
public decimal Width
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public decimal Height
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public SizeInfo Negate() => new(-this.Width, -this.Height);
|
||||
|
||||
public SizeInfo Shrink(PaddingInfo padding) => new(this.Width - padding.Horizontal, this.Height - padding.Vertical);
|
||||
|
||||
public SizeInfo Intersect(SizeInfo size) => new(
|
||||
Math.Min(this.Width, size.Width),
|
||||
Math.Min(this.Height, size.Height));
|
||||
|
||||
public RectangleInfo PlaceAt(decimal x, decimal y) => new(x, y, this.Width, this.Height);
|
||||
|
||||
public SizeInfo ScaleToFit(SizeInfo bounds)
|
||||
{
|
||||
var widthRatio = bounds.Width / this.Width;
|
||||
var heightRatio = bounds.Height / this.Height;
|
||||
return widthRatio.CompareTo(heightRatio) switch
|
||||
{
|
||||
< 0 => new(bounds.Width, this.Height * widthRatio),
|
||||
0 => bounds,
|
||||
> 0 => new(this.Width * heightRatio, bounds.Height),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the scaling ratio to scale obj by so that it fits inside the specified bounds
|
||||
/// without distorting the aspect ratio.
|
||||
/// </summary>
|
||||
public decimal ScaleToFitRatio(SizeInfo bounds)
|
||||
{
|
||||
if (bounds.Width == 0 || bounds.Height == 0)
|
||||
{
|
||||
throw new ArgumentException($"{nameof(bounds.Width)} or {nameof(bounds.Height)} cannot be zero", nameof(bounds));
|
||||
}
|
||||
|
||||
var widthRatio = bounds.Width / this.Width;
|
||||
var heightRatio = bounds.Height / this.Height;
|
||||
var scalingRatio = Math.Min(widthRatio, heightRatio);
|
||||
|
||||
return scalingRatio;
|
||||
}
|
||||
|
||||
public Size ToSize() => new((int)this.Width, (int)this.Height);
|
||||
|
||||
public Point ToPoint() => new((int)this.Width, (int)this.Height);
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "{" +
|
||||
$"{nameof(this.Width)}={this.Width}," +
|
||||
$"{nameof(this.Height)}={this.Height}" +
|
||||
"}";
|
||||
}
|
||||
}
|
||||
123
src/modules/MouseUtils/MouseJumpUI/Models/Layout/LayoutConfig.cs
Normal file
123
src/modules/MouseUtils/MouseJumpUI/Models/Layout/LayoutConfig.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
// 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 MouseJumpUI.Models.Drawing;
|
||||
using MouseJumpUI.Models.Screen;
|
||||
|
||||
namespace MouseJumpUI.Models.Layout;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a collection of values needed for calculating the MainForm layout.
|
||||
/// </summary>
|
||||
public sealed class LayoutConfig
|
||||
{
|
||||
public LayoutConfig(
|
||||
RectangleInfo virtualScreenBounds,
|
||||
List<ScreenInfo> screens,
|
||||
PointInfo activatedLocation,
|
||||
int activatedScreenIndex,
|
||||
int activatedScreenNumber,
|
||||
SizeInfo maximumFormSize,
|
||||
PaddingInfo formPadding,
|
||||
PaddingInfo previewPadding)
|
||||
{
|
||||
// make sure the virtual screen entirely contains all of the individual screen bounds
|
||||
ArgumentNullException.ThrowIfNull(virtualScreenBounds);
|
||||
ArgumentNullException.ThrowIfNull(screens);
|
||||
if (screens.Any(screen => !virtualScreenBounds.Contains(screen.Bounds)))
|
||||
{
|
||||
throw new ArgumentException($"'{nameof(virtualScreenBounds)}' must contain all of the screens in '{nameof(screens)}'", nameof(virtualScreenBounds));
|
||||
}
|
||||
|
||||
this.VirtualScreenBounds = virtualScreenBounds;
|
||||
this.Screens = new(screens.ToList());
|
||||
this.ActivatedLocation = activatedLocation;
|
||||
this.ActivatedScreenIndex = activatedScreenIndex;
|
||||
this.ActivatedScreenNumber = activatedScreenNumber;
|
||||
this.MaximumFormSize = maximumFormSize;
|
||||
this.FormPadding = formPadding;
|
||||
this.PreviewPadding = previewPadding;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the coordinates of the entire virtual screen.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The Virtual Screen is the bounding rectangle of all the monitors.
|
||||
/// https://learn.microsoft.com/en-us/windows/win32/gdi/the-virtual-screen
|
||||
/// </remarks>
|
||||
public RectangleInfo VirtualScreenBounds
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection containing the individual screens connected to the system.
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<ScreenInfo> Screens
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the point where the cursor was located when the form was activated.
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// The preview form will be centered on this location unless there are any
|
||||
/// constraints such as being too close to edge of a screen, in which case
|
||||
/// the form will be displayed centered as close as possible to this location.
|
||||
/// </summary>
|
||||
public PointInfo ActivatedLocation
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of the screen the cursor was on when the form was activated.
|
||||
/// The value is an index into the ScreenBounds array and is 0-indexed as a result.
|
||||
/// </summary>
|
||||
public int ActivatedScreenIndex
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the screen number the cursor was on when the form was activated.
|
||||
/// The value matches the screen numbering scheme in the "Display Settings" dialog
|
||||
/// and is 1-indexed as a result.
|
||||
/// </summary>
|
||||
public int ActivatedScreenNumber
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum size of the screen preview form.
|
||||
/// </summary>
|
||||
public SizeInfo MaximumFormSize
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the padding border around the screen preview form.
|
||||
/// </summary>
|
||||
public PaddingInfo FormPadding
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the padding border inside the screen preview image.
|
||||
/// </summary>
|
||||
public PaddingInfo PreviewPadding
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
113
src/modules/MouseUtils/MouseJumpUI/Models/Layout/LayoutInfo.cs
Normal file
113
src/modules/MouseUtils/MouseJumpUI/Models/Layout/LayoutInfo.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
// 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 MouseJumpUI.Models.Drawing;
|
||||
|
||||
namespace MouseJumpUI.Models.Layout;
|
||||
|
||||
public sealed class LayoutInfo
|
||||
{
|
||||
public sealed class Builder
|
||||
{
|
||||
public Builder()
|
||||
{
|
||||
this.ScreenBounds = new();
|
||||
}
|
||||
|
||||
public LayoutConfig? LayoutConfig
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public RectangleInfo? FormBounds
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public RectangleInfo? PreviewBounds
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public List<RectangleInfo> ScreenBounds
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public RectangleInfo? ActivatedScreenBounds
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public LayoutInfo Build()
|
||||
{
|
||||
return new LayoutInfo(
|
||||
layoutConfig: this.LayoutConfig ?? throw new InvalidOperationException(),
|
||||
formBounds: this.FormBounds ?? throw new InvalidOperationException(),
|
||||
previewBounds: this.PreviewBounds ?? throw new InvalidOperationException(),
|
||||
screenBounds: this.ScreenBounds ?? throw new InvalidOperationException(),
|
||||
activatedScreenBounds: this.ActivatedScreenBounds ?? throw new InvalidOperationException());
|
||||
}
|
||||
}
|
||||
|
||||
public LayoutInfo(
|
||||
LayoutConfig layoutConfig,
|
||||
RectangleInfo formBounds,
|
||||
RectangleInfo previewBounds,
|
||||
IEnumerable<RectangleInfo> screenBounds,
|
||||
RectangleInfo activatedScreenBounds)
|
||||
{
|
||||
this.LayoutConfig = layoutConfig ?? throw new ArgumentNullException(nameof(layoutConfig));
|
||||
this.FormBounds = formBounds ?? throw new ArgumentNullException(nameof(formBounds));
|
||||
this.PreviewBounds = previewBounds ?? throw new ArgumentNullException(nameof(previewBounds));
|
||||
this.ScreenBounds = new(
|
||||
(screenBounds ?? throw new ArgumentNullException(nameof(screenBounds)))
|
||||
.ToList());
|
||||
this.ActivatedScreenBounds = activatedScreenBounds ?? throw new ArgumentNullException(nameof(activatedScreenBounds));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the original LayoutConfig settings used to calculate coordinates.
|
||||
/// </summary>
|
||||
public LayoutConfig LayoutConfig
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size and location of the preview form.
|
||||
/// </summary>
|
||||
public RectangleInfo FormBounds
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the size and location of the preview image.
|
||||
/// </summary>
|
||||
public RectangleInfo PreviewBounds
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public ReadOnlyCollection<RectangleInfo> ScreenBounds
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public RectangleInfo ActivatedScreenBounds
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
using MouseJumpUI.Models.Drawing;
|
||||
|
||||
namespace MouseJumpUI.Models.Screen;
|
||||
|
||||
/// <summary>
|
||||
/// Immutable version of a System.Windows.Forms.Screen object so we don't need to
|
||||
/// take a dependency on WinForms just for screen info.
|
||||
/// </summary>
|
||||
public sealed class ScreenInfo
|
||||
{
|
||||
internal ScreenInfo(int handle, bool primary, RectangleInfo displayArea, RectangleInfo workingArea)
|
||||
{
|
||||
this.Handle = handle;
|
||||
this.Primary = primary;
|
||||
this.DisplayArea = displayArea ?? throw new ArgumentNullException(nameof(displayArea));
|
||||
this.WorkingArea = workingArea ?? throw new ArgumentNullException(nameof(workingArea));
|
||||
}
|
||||
|
||||
public int Handle
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public bool Primary
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public RectangleInfo DisplayArea
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
public RectangleInfo Bounds =>
|
||||
this.DisplayArea;
|
||||
|
||||
public RectangleInfo WorkingArea
|
||||
{
|
||||
get;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,9 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Threading;
|
||||
@@ -95,4 +97,29 @@ internal static class Program
|
||||
cancellationTokenSource.Cancel();
|
||||
Application.Exit();
|
||||
}
|
||||
|
||||
private static MouseJumpSettings ReadSettings()
|
||||
{
|
||||
var settingsUtils = new SettingsUtils();
|
||||
var settingsPath = settingsUtils.GetSettingsFilePath(MouseJumpSettings.ModuleName);
|
||||
if (!File.Exists(settingsPath))
|
||||
{
|
||||
var scaffoldSettings = new MouseJumpSettings();
|
||||
settingsUtils.SaveSettings(JsonSerializer.Serialize(scaffoldSettings), MouseJumpSettings.ModuleName);
|
||||
}
|
||||
|
||||
var settings = new MouseJumpSettings();
|
||||
try
|
||||
{
|
||||
settings = settingsUtils.GetSettings<MouseJumpSettings>(MouseJumpSettings.ModuleName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var errorMessage = $"There was a problem reading the configuration file. Error: {ex.GetType()} {ex.Message}";
|
||||
Logger.LogInfo(errorMessage);
|
||||
Logger.LogDebug(errorMessage);
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<Target Name="GenerateResourceFiles" BeforeTargets="PrepareForBuild">
|
||||
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted $(SolutionDir)tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory) resource.base.h resource.h new.base.rc new.rc" />
|
||||
</Target>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>17.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{0db0f63a-d2f8-4da3-a650-2d0b8724218e}</ProjectGuid>
|
||||
<RootNamespace>NewPlusShellExtensionWin10</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.22621.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps\</OutDir>
|
||||
<TargetName>PowerToys.NewPlus.ShellExtension.win10</TargetName>
|
||||
<LinkIncremental />
|
||||
<IgnoreImportLibrary />
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;NEWPLUSSHELLEXTENSIONWIN10_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<AdditionalIncludeDirectories>..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories);..\NewShellExtensionContextMenu</AdditionalIncludeDirectories>
|
||||
<CompileAsWinRT>false</CompileAsWinRT>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<AdditionalDependencies>runtimeobject.lib;$(CoreLibraryDependencies)</AdditionalDependencies>
|
||||
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;NEWPLUSSHELLEXTENSIONWIN10_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<AdditionalIncludeDirectories>..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories);..\NewShellExtensionContextMenu</AdditionalIncludeDirectories>
|
||||
<CompileAsWinRT>false</CompileAsWinRT>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<AdditionalDependencies>runtimeobject.lib;$(CoreLibraryDependencies)</AdditionalDependencies>
|
||||
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\constants.h" />
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\new_utilities.h" />
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\settings.h" />
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\shell_context_sub_menu.h" />
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\shell_context_sub_menu_item.h" />
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\template_folder.h" />
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\template_item.h" />
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\trace.h" />
|
||||
<ClInclude Include="dll_main.h" />
|
||||
<ClInclude Include="Generated Files\resource.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="resource.base.h" />
|
||||
<ClInclude Include="shell_context_menu_win10.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\new_utilities.cpp" />
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\powertoys_module.cpp" />
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\settings.cpp" />
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\shell_context_sub_menu.cpp" />
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\shell_context_sub_menu_item.cpp" />
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\template_folder.cpp" />
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\template_item.cpp" />
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\trace.cpp" />
|
||||
<ClCompile Include="dll_main.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="shell_context_menu_win10.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="Generated Files\new.rc" />
|
||||
<None Include="new.base.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="resources.resx">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\common\Telemetry\EtwTrace\EtwTrace.vcxproj">
|
||||
<Project>{8f021b46-362b-485c-bfba-ccf83e820cbd}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\common\Themes\Themes.vcxproj">
|
||||
<Project>{98537082-0fdb-40de-abd8-0dc5a4269bab}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="dll.def" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -1,116 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Generated Files">
|
||||
<UniqueIdentifier>{4cea4fff-ccef-4b62-9e46-f33da2b9a0cc}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="shell_context_menu_win10.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resource.base.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Generated Files\resource.h">
|
||||
<Filter>Generated Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\new_utilities.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\shell_context_sub_menu.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\shell_context_sub_menu_item.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\template_folder.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\template_item.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\trace.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\constants.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="dll_main.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\settings.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="dll_main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="shell_context_menu_win10.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\template_item.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\template_folder.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\shell_context_sub_menu.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\shell_context_sub_menu_item.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\powertoys_module.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\settings.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\trace.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\new_utilities.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="Generated Files\new.rc">
|
||||
<Filter>Generated Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
<None Include="dll.def">
|
||||
<Filter>Source Files</Filter>
|
||||
</None>
|
||||
<None Include="new.base.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</None>
|
||||
<None Include="resources.resx">
|
||||
<Filter>Resource Files</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,6 +0,0 @@
|
||||
LIBRARY
|
||||
|
||||
EXPORTS
|
||||
DllGetClassObject PRIVATE
|
||||
DllCanUnloadNow PRIVATE
|
||||
DllGetActivationFactory PRIVATE
|
||||
@@ -1,44 +0,0 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "shell_context_menu_win10.h"
|
||||
#include "dll_main.h"
|
||||
#include "trace.h"
|
||||
|
||||
#include <common/Telemetry/EtwTrace/EtwTrace.h>
|
||||
|
||||
HMODULE module_instance_handle = 0;
|
||||
Shared::Trace::ETWTrace trace(L"NewPlusShellExtension_Win10");
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE module_handle, DWORD ul_reason_for_call, LPVOID reserved)
|
||||
{
|
||||
switch (ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
module_instance_handle = module_handle;
|
||||
Trace::RegisterProvider();
|
||||
newplus::utilities::init_logger();
|
||||
break;
|
||||
|
||||
case DLL_PROCESS_DETACH:
|
||||
Trace::UnregisterProvider();
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
STDAPI DllGetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ IActivationFactory** factory)
|
||||
{
|
||||
return Module<ModuleType::InProc>::GetModule().GetActivationFactory(activatableClassId, factory);
|
||||
}
|
||||
|
||||
STDAPI DllCanUnloadNow()
|
||||
{
|
||||
return Module<InProc>::GetModule().GetObjectCount() == 0 ? S_OK : S_FALSE;
|
||||
}
|
||||
|
||||
STDAPI DllGetClassObject(_In_ REFCLSID ref_class_id, _In_ REFIID ref_interface_id, _Outptr_ LPVOID FAR* object)
|
||||
{
|
||||
return Module<InProc>::GetModule().GetClassObject(ref_class_id, ref_interface_id, object);
|
||||
}
|
||||
|
||||
CoCreatableClass(shell_context_menu_win10)
|
||||
@@ -1,6 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/Telemetry/EtwTrace/EtwTrace.h>
|
||||
|
||||
extern HMODULE module_instance_handle;
|
||||
extern Shared::Trace::ETWTrace trace;
|
||||
@@ -1,54 +0,0 @@
|
||||
#include <windows.h>
|
||||
#include "Generated Files/resource.h"
|
||||
#include "../../../../common/version/version.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
#include "winres.h"
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION FILE_VERSION
|
||||
PRODUCTVERSION PRODUCT_VERSION
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x2L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", COMPANY_NAME
|
||||
VALUE "FileDescription", FILE_DESCRIPTION
|
||||
VALUE "FileVersion", FILE_VERSION_STRING
|
||||
VALUE "InternalName", INTERNAL_NAME
|
||||
VALUE "LegalCopyright", COPYRIGHT_NOTE
|
||||
VALUE "OriginalFilename", ORIGINAL_FILENAME
|
||||
VALUE "ProductName", PRODUCT_NAME
|
||||
VALUE "ProductVersion", PRODUCT_VERSION_STRING
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Icon
|
||||
//
|
||||
|
||||
// Icon with lowest ID value placed first to ensure application icon
|
||||
// remains consistent on all systems.
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -1,3 +0,0 @@
|
||||
// pch.cpp: source file corresponding to the pre-compiled header
|
||||
|
||||
#include "pch.h"
|
||||
@@ -1,39 +0,0 @@
|
||||
// Precompiled header file.
|
||||
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMCX
|
||||
#define NOHELP
|
||||
#define NOCOMM
|
||||
|
||||
// Windows and STL
|
||||
#include <Shobjidl.h>
|
||||
#include <shlwapi.h>
|
||||
#include <shellapi.h>
|
||||
#include <Windows.h>
|
||||
#include <shlobj.h>
|
||||
#include <vector>
|
||||
#include <system_error>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <atlbase.h>
|
||||
#include <wrl.h>
|
||||
#include <wrl/module.h>
|
||||
#include <wrl/client.h>
|
||||
#include <unknwn.h>
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
// PowerToys project common
|
||||
#include <ProjectTelemetry.h>
|
||||
#include <common/utils/resources.h>
|
||||
#include <common/logger/logger.h>
|
||||
#include <common/logger/logger_settings.h>
|
||||
#include <common/utils/logger_helper.h>
|
||||
#include <common/Themes/theme_helpers.h>
|
||||
|
||||
// New project specific
|
||||
#include "dll_main.h"
|
||||
#include "template_folder.h"
|
||||
#include "settings.h"
|
||||
#include "new_utilities.h"
|
||||
@@ -1,12 +0,0 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
|
||||
//////////////////////////////
|
||||
// Non-localizable
|
||||
|
||||
#define FILE_DESCRIPTION "PowerToys.New+"
|
||||
#define INTERNAL_NAME "PowerToys.New+"
|
||||
#define ORIGINAL_FILENAME "PowerToys.NewPlus.ShellExtension.win10.dll"
|
||||
|
||||
// Non-localizable
|
||||
//////////////////////////////
|
||||
@@ -1,132 +0,0 @@
|
||||
<?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="context_menu_item_new" xml:space="preserve">
|
||||
<value>New+</value>
|
||||
<comment>The main context menu item that users click on. This should be localized to match New in Windows. e.g. Danish it would become Ny+</comment>
|
||||
</data>
|
||||
<data name="context_menu_item_open_templates" xml:space="preserve">
|
||||
<value>Open templates</value>
|
||||
<comment>The menu item in the context menu that enables user to open the folder that contains their templates.</comment>
|
||||
</data>
|
||||
<data name="default_template_sub_folder_name_where_templates_are_stored" xml:space="preserve">
|
||||
<value>Templates</value>
|
||||
<comment>Default subfolder name where templates are stored.</comment>
|
||||
</data>
|
||||
</root>
|
||||
@@ -1,270 +0,0 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "shell_context_menu_win10.h"
|
||||
#include "shell_context_sub_menu.h"
|
||||
#include "shell_context_sub_menu_item.h"
|
||||
#include "new_utilities.h"
|
||||
#include "settings.h"
|
||||
#include "trace.h"
|
||||
#include "Generated Files/resource.h"
|
||||
#include <common/Themes/icon_helpers.h>
|
||||
#include <common/utils/winapi_error.h>
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace newplus;
|
||||
|
||||
shell_context_menu_win10::~shell_context_menu_win10()
|
||||
{
|
||||
for (const auto& handle : bitmap_handles)
|
||||
{
|
||||
DeleteObject(handle);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma region IShellExtInit
|
||||
IFACEMETHODIMP shell_context_menu_win10::Initialize(PCIDLIST_ABSOLUTE, IDataObject*, HKEY)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
#pragma region IContextMenu
|
||||
IFACEMETHODIMP shell_context_menu_win10::QueryContextMenu(HMENU menu_handle, UINT menu_index, UINT menu_first_cmd_id, UINT, UINT menu_flags)
|
||||
{
|
||||
if (!NewSettingsInstance().GetEnabled()
|
||||
|| package::IsWin11OrGreater()
|
||||
)
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
if (menu_flags & CMF_DEFAULTONLY)
|
||||
{
|
||||
return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Create the initial context popup menu containing the list of templates and open templates action
|
||||
int menu_id = menu_first_cmd_id;
|
||||
MENUITEMINFO newplus_main_context_menu_item;
|
||||
HMENU sub_menu_of_templates = CreatePopupMenu();
|
||||
int sub_menu_index = 0;
|
||||
|
||||
// Determine the New+ Template folder location
|
||||
const std::filesystem::path template_folder_root = utilities::get_new_template_folder_location();
|
||||
|
||||
// Create the New+ Template folder location if it doesn't exist (very rare scenario)
|
||||
utilities::create_folder_if_not_exist(template_folder_root);
|
||||
|
||||
// Scan the folder for any files and folders (the templates)
|
||||
templates = new template_folder(template_folder_root);
|
||||
templates->rescan_template_folder();
|
||||
const auto number_of_templates = templates->list_of_templates.size();
|
||||
|
||||
// Create the New+ menu item and point to the initial context popup menu
|
||||
static const std::wstring localized_context_menu_item =
|
||||
GET_RESOURCE_STRING_FALLBACK(IDS_CONTEXT_MENU_ITEM_NEW, L"New+");
|
||||
wchar_t newplus_menu_name[20] = { 0 };
|
||||
wcscpy_s(newplus_menu_name, ARRAYSIZE(newplus_menu_name), localized_context_menu_item.c_str());
|
||||
newplus_main_context_menu_item.cbSize = sizeof(MENUITEMINFOW);
|
||||
newplus_main_context_menu_item.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_SUBMENU;
|
||||
newplus_main_context_menu_item.wID = menu_id;
|
||||
newplus_main_context_menu_item.fType = MFT_STRING;
|
||||
newplus_main_context_menu_item.dwTypeData = (PWSTR)newplus_menu_name;
|
||||
newplus_main_context_menu_item.hSubMenu = sub_menu_of_templates;
|
||||
const auto newplus_icon_index = 0;
|
||||
|
||||
if (bitmap_handles.size() == 0)
|
||||
{
|
||||
const std::wstring icon_file = utilities::get_new_icon_resource_filepath(
|
||||
module_instance_handle, ThemeHelpers::GetAppTheme())
|
||||
.c_str();
|
||||
HICON local_icon_handle = static_cast<HICON>(
|
||||
LoadImage(NULL, icon_file.c_str(), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_LOADFROMFILE));
|
||||
|
||||
if (local_icon_handle)
|
||||
{
|
||||
bitmap_handles.push_back(CreateBitmapFromIcon(local_icon_handle));
|
||||
DestroyIcon(local_icon_handle);
|
||||
}
|
||||
}
|
||||
if (bitmap_handles.size() > newplus_icon_index && bitmap_handles[newplus_icon_index])
|
||||
{
|
||||
newplus_main_context_menu_item.fMask |= MIIM_BITMAP;
|
||||
newplus_main_context_menu_item.hbmpItem = bitmap_handles[newplus_icon_index];
|
||||
}
|
||||
|
||||
menu_id++;
|
||||
|
||||
// Add template items to context menu
|
||||
int index = 0;
|
||||
for (; index < number_of_templates; index++)
|
||||
{
|
||||
const auto template_item = templates->get_template_item(index);
|
||||
add_template_item_to_context_menu(sub_menu_of_templates, sub_menu_index, template_item, menu_id, index);
|
||||
menu_id++;
|
||||
sub_menu_index++;
|
||||
}
|
||||
|
||||
// Add separator to context menu
|
||||
add_separator_to_context_menu(sub_menu_of_templates, sub_menu_index);
|
||||
sub_menu_index++;
|
||||
|
||||
// Add "Open templates" item to context menu
|
||||
add_open_templates_to_context_menu(sub_menu_of_templates, sub_menu_index, template_folder_root, menu_id, index);
|
||||
menu_id++;
|
||||
|
||||
if (!InsertMenuItem(menu_handle, menu_index, TRUE, &newplus_main_context_menu_item))
|
||||
{
|
||||
Logger::error(L"QueryContextMenu() failed. {}", get_last_error_or_default(GetLastError()));
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return the amount if entries inserted
|
||||
const auto number_of_items_inserted = menu_id - menu_first_cmd_id;
|
||||
return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, number_of_items_inserted);
|
||||
}
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
Logger::error(ex.what());
|
||||
}
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
void shell_context_menu_win10::add_open_templates_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index, const std::filesystem::path& template_folder_root, int menu_id, int index)
|
||||
{
|
||||
static const std::wstring localized_context_menu_item_open_templates =
|
||||
GET_RESOURCE_STRING_FALLBACK(IDS_CONTEXT_MENU_ITEM_OPEN_TEMPLATES, L"Open templates");
|
||||
wchar_t menu_name_open[256] = { 0 };
|
||||
wcscpy_s(menu_name_open, ARRAYSIZE(menu_name_open), localized_context_menu_item_open_templates.c_str());
|
||||
const auto open_folder_item = Make<template_folder_context_menu_item>(template_folder_root);
|
||||
MENUITEMINFO newplus_menu_item_open_templates;
|
||||
newplus_menu_item_open_templates.cbSize = sizeof(MENUITEMINFO);
|
||||
newplus_menu_item_open_templates.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID;
|
||||
newplus_menu_item_open_templates.wID = menu_id;
|
||||
newplus_menu_item_open_templates.fType = MFT_STRING;
|
||||
newplus_menu_item_open_templates.dwTypeData = (PWSTR)menu_name_open;
|
||||
|
||||
const auto open_templates_icon_index = index + 1;
|
||||
if (bitmap_handles.size() <= open_templates_icon_index)
|
||||
{
|
||||
const std::wstring icon_file = utilities::get_open_templates_icon_resource_filepath(
|
||||
module_instance_handle, ThemeHelpers::GetAppTheme())
|
||||
.c_str();
|
||||
HICON open_template_icon_handle = static_cast<HICON>(
|
||||
LoadImage(NULL, icon_file.c_str(), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_LOADFROMFILE));
|
||||
if (open_template_icon_handle)
|
||||
{
|
||||
bitmap_handles.push_back(CreateBitmapFromIcon(open_template_icon_handle));
|
||||
DestroyIcon(open_template_icon_handle);
|
||||
}
|
||||
}
|
||||
if (bitmap_handles.size() > open_templates_icon_index && bitmap_handles[open_templates_icon_index])
|
||||
{
|
||||
newplus_menu_item_open_templates.fMask |= MIIM_BITMAP;
|
||||
newplus_menu_item_open_templates.hbmpItem = bitmap_handles[open_templates_icon_index];
|
||||
}
|
||||
|
||||
InsertMenuItem(sub_menu_of_templates, sub_menu_index, TRUE, &newplus_menu_item_open_templates);
|
||||
}
|
||||
|
||||
void shell_context_menu_win10::add_separator_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index)
|
||||
{
|
||||
MENUITEMINFO menu_item_separator;
|
||||
menu_item_separator.cbSize = sizeof(MENUITEMINFO);
|
||||
menu_item_separator.fMask = MIIM_FTYPE;
|
||||
menu_item_separator.fType = MFT_SEPARATOR;
|
||||
InsertMenuItem(sub_menu_of_templates, sub_menu_index, TRUE, &menu_item_separator);
|
||||
}
|
||||
|
||||
void shell_context_menu_win10::add_template_item_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index, newplus::template_item* const template_item, int menu_id, int index)
|
||||
{
|
||||
wchar_t menu_name[256] = { 0 };
|
||||
wcscpy_s(menu_name, ARRAYSIZE(menu_name), template_item->get_menu_title(!utilities::get_newplus_setting_hide_extension(), !utilities::get_newplus_setting_hide_starting_digits()).c_str());
|
||||
MENUITEMINFO newplus_menu_item_template;
|
||||
newplus_menu_item_template.cbSize = sizeof(MENUITEMINFO);
|
||||
newplus_menu_item_template.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_DATA;
|
||||
newplus_menu_item_template.wID = menu_id;
|
||||
newplus_menu_item_template.fType = MFT_STRING;
|
||||
newplus_menu_item_template.dwTypeData = (PWSTR)menu_name;
|
||||
const auto current_template_icon_index = index + 1;
|
||||
if (bitmap_handles.size() <= current_template_icon_index)
|
||||
{
|
||||
HICON template_icon_handle = template_item->get_explorer_icon_handle();
|
||||
if (template_icon_handle)
|
||||
{
|
||||
bitmap_handles.push_back(CreateBitmapFromIcon(template_icon_handle));
|
||||
DestroyIcon(template_icon_handle);
|
||||
}
|
||||
}
|
||||
if (bitmap_handles.size() > current_template_icon_index && bitmap_handles[current_template_icon_index])
|
||||
{
|
||||
newplus_menu_item_template.fMask |= MIIM_BITMAP;
|
||||
newplus_menu_item_template.hbmpItem = bitmap_handles[current_template_icon_index];
|
||||
}
|
||||
|
||||
InsertMenuItem(sub_menu_of_templates, sub_menu_index, TRUE, &newplus_menu_item_template);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP shell_context_menu_win10::InvokeCommand(CMINVOKECOMMANDINFO* params)
|
||||
{
|
||||
if (!params)
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
// Get selected menu item (a template or the "Open templates" item)
|
||||
const auto selected_menu_item_index = LOWORD(params->lpVerb) - 1;
|
||||
if (selected_menu_item_index < 0)
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
const auto number_of_templates = templates->list_of_templates.size();
|
||||
const bool is_template_item = selected_menu_item_index < number_of_templates;
|
||||
|
||||
// Save how many item templates we have so it can be sent later when we do something with New+.
|
||||
// It will be sent when the user does something, similar to Windows 11 context menu.
|
||||
newplus::utilities::set_saved_number_of_templates(static_cast<size_t>(number_of_templates));
|
||||
|
||||
if (is_template_item)
|
||||
{
|
||||
// It's a template menu item
|
||||
const auto template_entry = templates->get_template_item(selected_menu_item_index);
|
||||
|
||||
return newplus::utilities::copy_template(template_entry, site_of_folder);
|
||||
}
|
||||
else
|
||||
{
|
||||
// It's the "Open templates" menu item
|
||||
const std::filesystem::path template_folder_root = utilities::get_new_template_folder_location();
|
||||
|
||||
return newplus::utilities::open_template_folder(template_folder_root);
|
||||
}
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP shell_context_menu_win10::GetCommandString(UINT_PTR, UINT, UINT*, CHAR*, UINT)
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
#pragma region IObjectWithSite
|
||||
IFACEMETHODIMP shell_context_menu_win10::SetSite(_In_ IUnknown* site) noexcept
|
||||
{
|
||||
this->site_of_folder = site;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
IFACEMETHODIMP shell_context_menu_win10::GetSite(_In_ REFIID riid, _COM_Outptr_ void** returned_site) noexcept
|
||||
{
|
||||
return this->site_of_folder.CopyTo(riid, returned_site);
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
#include <template_folder.h>
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
#define NEW_SHELL_EXTENSION_EXPLORER_COMMAND_WIN10_UUID "FF90D477-E32A-4BE8-8CC5-A502A97F5401"
|
||||
|
||||
// File Explorer context menu "New+" for Windows 10
|
||||
class __declspec(uuid(NEW_SHELL_EXTENSION_EXPLORER_COMMAND_WIN10_UUID)) shell_context_menu_win10 :
|
||||
public RuntimeClass<
|
||||
RuntimeClassFlags<ClassicCom>,
|
||||
IShellExtInit,
|
||||
IContextMenu,
|
||||
IObjectWithSite>
|
||||
{
|
||||
public:
|
||||
~shell_context_menu_win10();
|
||||
|
||||
#pragma region IShellExtInit
|
||||
IFACEMETHODIMP Initialize(_In_opt_ PCIDLIST_ABSOLUTE, _In_ IDataObject*, HKEY);
|
||||
#pragma endregion
|
||||
|
||||
#pragma region IContextMenu
|
||||
IFACEMETHODIMP QueryContextMenu(HMENU menu_handle, UINT menu_index, UINT menu_first_cmd_id, UINT, UINT menu_flags);
|
||||
IFACEMETHODIMP InvokeCommand(CMINVOKECOMMANDINFO* pici);
|
||||
IFACEMETHODIMP GetCommandString(UINT_PTR, UINT, UINT*, CHAR*, UINT);
|
||||
#pragma endregion
|
||||
|
||||
#pragma region IObjectWithSite
|
||||
IFACEMETHODIMP SetSite(_In_ IUnknown* site) noexcept;
|
||||
IFACEMETHODIMP GetSite(_In_ REFIID riid, _COM_Outptr_ void** site) noexcept;
|
||||
#pragma endregion
|
||||
|
||||
protected:
|
||||
void add_open_templates_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index, const std::filesystem::path& template_folder_root, int menu_id, int index);
|
||||
void add_separator_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index);
|
||||
void add_template_item_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index, newplus::template_item* const template_item, int menu_id, int index);
|
||||
|
||||
HINSTANCE instance_handle = 0;
|
||||
ComPtr<IUnknown> site_of_folder;
|
||||
newplus::template_folder* templates = nullptr;
|
||||
std::vector<HBITMAP> bitmap_handles;
|
||||
};
|
||||
@@ -128,7 +128,6 @@ MakeAppx.exe pack /d . /p $(OutDir)NewPlusPackage.msix /nv</Command>
|
||||
<ClInclude Include="template_item.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="new_utilities.cpp" />
|
||||
<ClCompile Include="shell_context_menu.cpp" />
|
||||
<ClCompile Include="shell_context_sub_menu.cpp" />
|
||||
<ClCompile Include="shell_context_sub_menu_item.cpp" />
|
||||
@@ -228,7 +227,7 @@ MakeAppx.exe pack /d . /p $(OutDir)NewPlusPackage.msix /nv</Command>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<Import Project="..\..\..\..\deps\spdlog.props" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -31,9 +31,6 @@
|
||||
<ClCompile Include="trace.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="new_utilities.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="template_folder.h">
|
||||
@@ -126,6 +123,9 @@
|
||||
<None Include="AppxManifest.xml">
|
||||
<Filter>Source Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\New.ico">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\SmallTile.png">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
@@ -190,8 +190,14 @@
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CopyFileToFolders Include="TemplateExamples\Any files or folders placed in the template folder are available via New+.txt" />
|
||||
<CopyFileToFolders Include="TemplateExamples\Example folder\Example txt file.txt" />
|
||||
<CopyFileToFolders Include="TemplateExamples\Example folder\Another example txt file.txt" />
|
||||
<Text Include="TemplateExamples\Example folder\Another example txt file.txt">
|
||||
<Filter>Template Examples\Example folder</Filter>
|
||||
</Text>
|
||||
<Text Include="TemplateExamples\Example folder\Example txt file.txt">
|
||||
<Filter>Template Examples\Example folder</Filter>
|
||||
</Text>
|
||||
<Text Include="TemplateExamples\Any files or folders placed in the template folder are available via New+.txt">
|
||||
<Filter>Template Examples</Filter>
|
||||
</Text>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,13 +0,0 @@
|
||||
#include "pch.h"
|
||||
#include "new_utilities.h"
|
||||
|
||||
// HACK: Store number of templates when generating the menu entries to send later.
|
||||
size_t saved_number_of_templates = -1;
|
||||
size_t newplus::utilities::get_saved_number_of_templates()
|
||||
{
|
||||
return saved_number_of_templates;
|
||||
}
|
||||
void newplus::utilities::set_saved_number_of_templates(size_t templates)
|
||||
{
|
||||
saved_number_of_templates = templates;
|
||||
}
|
||||
@@ -7,17 +7,11 @@
|
||||
|
||||
#include "constants.h"
|
||||
#include "settings.h"
|
||||
#include "template_item.h"
|
||||
#include "trace.h"
|
||||
|
||||
#pragma comment(lib, "Shlwapi.lib")
|
||||
|
||||
using namespace newplus;
|
||||
|
||||
namespace newplus::utilities
|
||||
{
|
||||
size_t get_saved_number_of_templates();
|
||||
void set_saved_number_of_templates(size_t templates);
|
||||
|
||||
inline std::wstring get_explorer_icon(std::filesystem::path path)
|
||||
{
|
||||
@@ -45,33 +39,6 @@ namespace newplus::utilities
|
||||
return icon_resource;
|
||||
}
|
||||
|
||||
inline HICON get_explorer_icon_handle(std::filesystem::path path)
|
||||
{
|
||||
SHFILEINFO shell_file_info = { 0 };
|
||||
const std::wstring filepath = path.wstring();
|
||||
DWORD_PTR result = SHGetFileInfo(filepath.c_str(), 0, &shell_file_info, sizeof(shell_file_info), SHGFI_ICON);
|
||||
if (shell_file_info.hIcon)
|
||||
{
|
||||
return shell_file_info.hIcon;
|
||||
}
|
||||
|
||||
WCHAR icon_resource_specifier[MAX_PATH] = { 0 };
|
||||
DWORD buffer_length = MAX_PATH;
|
||||
const std::wstring extension = path.extension().wstring();
|
||||
const HRESULT hr = AssocQueryString(ASSOCF_INIT_IGNOREUNKNOWN,
|
||||
ASSOCSTR_DEFAULTICON,
|
||||
extension.c_str(),
|
||||
NULL,
|
||||
icon_resource_specifier,
|
||||
&buffer_length);
|
||||
const std::wstring icon_resource = icon_resource_specifier;
|
||||
|
||||
const auto icon_x = GetSystemMetrics(SM_CXSMICON);
|
||||
const auto icon_y = GetSystemMetrics(SM_CYSMICON);
|
||||
HICON hIcon = static_cast<HICON>(LoadImage(NULL, icon_resource.c_str(), IMAGE_ICON, icon_x, icon_y, LR_LOADFROMFILE));
|
||||
return hIcon;
|
||||
}
|
||||
|
||||
inline bool is_hidden(const std::filesystem::path path)
|
||||
{
|
||||
const std::filesystem::path::string_type name = path.filename();
|
||||
@@ -213,227 +180,4 @@ namespace newplus::utilities
|
||||
return path;
|
||||
}
|
||||
|
||||
inline bool is_desktop_folder(const std::filesystem::path target_fullpath)
|
||||
{
|
||||
TCHAR desktopPath[MAX_PATH];
|
||||
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, desktopPath)))
|
||||
{
|
||||
return StrCmpIW(target_fullpath.c_str(), desktopPath) == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void explorer_enter_rename_mode(const std::filesystem::path target_fullpath_of_new_instance)
|
||||
{
|
||||
const std::filesystem::path path_without_new_file_or_dir = target_fullpath_of_new_instance.parent_path();
|
||||
const std::filesystem::path new_file_or_dir_without_path = target_fullpath_of_new_instance.filename();
|
||||
|
||||
ComPtr<IShellWindows> shell_windows;
|
||||
|
||||
HRESULT hr;
|
||||
if (FAILED(CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_ALL, IID_PPV_ARGS(&shell_windows))))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
long window_handle;
|
||||
ComPtr<IDispatch> shell_window;
|
||||
const bool object_created_on_desktop = is_desktop_folder(path_without_new_file_or_dir.c_str());
|
||||
if (object_created_on_desktop)
|
||||
{
|
||||
// Special handling for desktop folder
|
||||
VARIANT empty_yet_needed_incl_init;
|
||||
VariantInit(&empty_yet_needed_incl_init);
|
||||
|
||||
if (FAILED(shell_windows->FindWindowSW(&empty_yet_needed_incl_init, &empty_yet_needed_incl_init, SWC_DESKTOP, &window_handle, SWFO_NEEDDISPATCH, &shell_window)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
long count_of_shell_windows = 0;
|
||||
shell_windows->get_Count(&count_of_shell_windows);
|
||||
|
||||
for (long i = 0; i < count_of_shell_windows; ++i)
|
||||
{
|
||||
ComPtr<IWebBrowserApp> web_browser_app;
|
||||
VARIANT v;
|
||||
V_VT(&v) = VT_I4;
|
||||
V_I4(&v) = i;
|
||||
hr = shell_windows->Item(v, &shell_window);
|
||||
if (SUCCEEDED(hr) && shell_window)
|
||||
{
|
||||
hr = shell_window.As(&web_browser_app);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
BSTR folder_view_location;
|
||||
hr = web_browser_app->get_LocationURL(&folder_view_location);
|
||||
if (SUCCEEDED(hr) && folder_view_location)
|
||||
{
|
||||
wchar_t path[MAX_PATH];
|
||||
DWORD pathLength = ARRAYSIZE(path);
|
||||
hr = PathCreateFromUrl(folder_view_location, path, &pathLength, 0);
|
||||
SysFreeString(folder_view_location);
|
||||
if (SUCCEEDED(hr) && StrCmpIW(path_without_new_file_or_dir.c_str(), path) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
shell_window = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!shell_window)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ComPtr<IServiceProvider> service_provider;
|
||||
shell_window.As(&service_provider);
|
||||
ComPtr<IShellBrowser> shell_browser;
|
||||
service_provider->QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&shell_browser));
|
||||
ComPtr<IShellView> shell_view;
|
||||
shell_browser->QueryActiveShellView(&shell_view);
|
||||
ComPtr<IFolderView> folder_view;
|
||||
shell_view.As(&folder_view);
|
||||
|
||||
// Find the newly created object (file or folder)
|
||||
// And put object into edit mode (SVSI_EDIT) and if desktop also reposition
|
||||
int number_of_objects_in_view = 0;
|
||||
bool done = false;
|
||||
folder_view->ItemCount(SVGIO_ALLVIEW, &number_of_objects_in_view);
|
||||
for (int i = 0; i < number_of_objects_in_view && !done; ++i)
|
||||
{
|
||||
std::wstring path_of_item(MAX_PATH, 0);
|
||||
LPITEMIDLIST shell_item_ids;
|
||||
|
||||
folder_view->Item(i, &shell_item_ids);
|
||||
SHGetPathFromIDList(shell_item_ids, &path_of_item[0]);
|
||||
|
||||
const std::wstring current_filename = std::filesystem::path(path_of_item.c_str()).filename();
|
||||
|
||||
if (utilities::wstring_same_when_comparing_ignore_case(new_file_or_dir_without_path, current_filename))
|
||||
{
|
||||
const DWORD common_select_flags = SVSI_EDIT | SVSI_SELECT | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE | SVSI_FOCUSED;
|
||||
|
||||
if (object_created_on_desktop)
|
||||
{
|
||||
// Newly created object is on the desktop -- reposition under mouse and enter rename mode
|
||||
LPCITEMIDLIST shell_item_to_select_and_position[] = { shell_item_ids };
|
||||
POINT mouse_position;
|
||||
GetCursorPos(&mouse_position);
|
||||
mouse_position.x -= GetSystemMetrics(SM_CXMENUSIZE);
|
||||
mouse_position.x = max(mouse_position.x, 20);
|
||||
mouse_position.y -= GetSystemMetrics(SM_CXMENUSIZE)/2;
|
||||
mouse_position.y = max(mouse_position.y, 20);
|
||||
POINT position[] = { mouse_position };
|
||||
folder_view->SelectAndPositionItems(1, shell_item_to_select_and_position, position, common_select_flags | SVSI_POSITIONITEM);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Enter rename mode
|
||||
folder_view->SelectItem(i, common_select_flags);
|
||||
}
|
||||
done = true;
|
||||
}
|
||||
CoTaskMemFree(shell_item_ids);
|
||||
}
|
||||
}
|
||||
|
||||
inline HRESULT copy_template(const template_item* template_entry, const ComPtr<IUnknown> site_of_folder)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
try
|
||||
{
|
||||
Logger::info(L"Copying template");
|
||||
|
||||
if (newplus::utilities::get_saved_number_of_templates() >= 0)
|
||||
{
|
||||
// Log that context menu was shown and with how many items
|
||||
trace.UpdateState(true);
|
||||
Trace::EventShowTemplateItems(newplus::utilities::get_saved_number_of_templates());
|
||||
trace.Flush();
|
||||
trace.UpdateState(false);
|
||||
}
|
||||
|
||||
// Determine target path of where context menu was displayed
|
||||
const auto target_path_name = utilities::get_path_from_unknown_site(site_of_folder);
|
||||
|
||||
// Determine initial filename
|
||||
std::filesystem::path source_fullpath = template_entry->path;
|
||||
std::filesystem::path target_fullpath = std::wstring(target_path_name);
|
||||
|
||||
// Only append name to target if source is not a directory
|
||||
if (!utilities::is_directory(source_fullpath))
|
||||
{
|
||||
target_fullpath.append(template_entry->get_target_filename(!utilities::get_newplus_setting_hide_starting_digits()));
|
||||
}
|
||||
|
||||
// Copy file and determine final filename
|
||||
std::filesystem::path target_final_fullpath = template_entry->copy_object_to(GetActiveWindow(), target_fullpath);
|
||||
|
||||
trace.UpdateState(true);
|
||||
Trace::EventCopyTemplate(target_final_fullpath.extension().c_str());
|
||||
trace.Flush();
|
||||
trace.UpdateState(false);
|
||||
|
||||
// Refresh folder items
|
||||
template_entry->refresh_target(target_final_fullpath);
|
||||
|
||||
// Enter rename mode
|
||||
template_entry->enter_rename_mode(target_final_fullpath);
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
Logger::error(ex.what());
|
||||
|
||||
hr = S_FALSE;
|
||||
}
|
||||
|
||||
trace.UpdateState(true);
|
||||
Trace::EventCopyTemplateResult(hr);
|
||||
trace.Flush();
|
||||
trace.UpdateState(false);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
inline HRESULT open_template_folder(const std::filesystem::path template_folder)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
try
|
||||
{
|
||||
Logger::info(L"Open templates folder");
|
||||
|
||||
if (newplus::utilities::get_saved_number_of_templates() >= 0)
|
||||
{
|
||||
// Log that context menu was shown and with how many items
|
||||
trace.UpdateState(true);
|
||||
Trace::EventShowTemplateItems(newplus::utilities::get_saved_number_of_templates());
|
||||
trace.Flush();
|
||||
trace.UpdateState(false);
|
||||
}
|
||||
|
||||
const std::wstring verb_hardcoded_do_not_change = L"open";
|
||||
ShellExecute(nullptr, verb_hardcoded_do_not_change.c_str(), template_folder.c_str(), NULL, NULL, SW_SHOWNORMAL);
|
||||
|
||||
trace.UpdateState(true);
|
||||
Trace::EventOpenTemplates();
|
||||
trace.Flush();
|
||||
trace.UpdateState(false);
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
Logger::error(ex.what());
|
||||
|
||||
hr = S_FALSE;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ public:
|
||||
|
||||
#pragma region IObjectWithSite
|
||||
IFACEMETHODIMP SetSite(_In_ IUnknown* site) noexcept;
|
||||
|
||||
IFACEMETHODIMP GetSite(_In_ REFIID riid, _COM_Outptr_ void** site) noexcept;
|
||||
#pragma endregion
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#include "pch.h"
|
||||
#include "shell_context_sub_menu.h"
|
||||
#include "trace.h"
|
||||
#include "new_utilities.h"
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
// // Sub context menu command enumerator
|
||||
shell_context_sub_menu::shell_context_sub_menu(const ComPtr<IUnknown> site_of_folder)
|
||||
{
|
||||
trace.UpdateState(true);
|
||||
this->site_of_folder = site_of_folder;
|
||||
|
||||
// Determine the New+ Template folder location
|
||||
@@ -36,9 +36,8 @@ shell_context_sub_menu::shell_context_sub_menu(const ComPtr<IUnknown> site_of_fo
|
||||
|
||||
current_command = explorer_menu_item_commands.cbegin();
|
||||
|
||||
// Save how many item templates we have so it can be sent later when we do something with New+.
|
||||
// We don't send it here or it would send an event every time we open a context menu.
|
||||
newplus::utilities::set_saved_number_of_templates(static_cast<size_t>(number_of_templates));
|
||||
// Log that context menu was shown and with how many items
|
||||
Trace::EventShowTemplateItems(number_of_templates);
|
||||
}
|
||||
|
||||
// IEnumExplorerCommand
|
||||
|
||||
@@ -63,7 +63,49 @@ IFACEMETHODIMP shell_context_sub_menu_item::GetState(_In_opt_ IShellItemArray* s
|
||||
|
||||
IFACEMETHODIMP shell_context_sub_menu_item::Invoke(_In_opt_ IShellItemArray*, _In_opt_ IBindCtx*) noexcept
|
||||
{
|
||||
return newplus::utilities::copy_template(template_entry, site_of_folder);
|
||||
HRESULT hr = S_OK;
|
||||
try
|
||||
{
|
||||
trace.UpdateState(true);
|
||||
|
||||
// Determine target path of where context menu was displayed
|
||||
const auto target_path_name = utilities::get_path_from_unknown_site(site_of_folder);
|
||||
|
||||
// Determine initial filename
|
||||
std::filesystem::path source_fullpath = template_entry->path;
|
||||
std::filesystem::path target_fullpath = std::wstring(target_path_name);
|
||||
|
||||
// Only append name to target if source is not a directory
|
||||
if (!utilities::is_directory(target_fullpath))
|
||||
{
|
||||
target_fullpath.append(this->template_entry->get_target_filename(!utilities::get_newplus_setting_hide_starting_digits()));
|
||||
}
|
||||
|
||||
// Copy file and determine final filename
|
||||
std::filesystem::path target_final_fullpath = this->template_entry->copy_object_to(GetActiveWindow(), target_fullpath);
|
||||
|
||||
Trace::EventCopyTemplate(target_final_fullpath.extension().c_str());
|
||||
|
||||
// Refresh folder items
|
||||
SHChangeNotify(SHCNE_CREATE, SHCNF_PATH | SHCNF_FLUSH, target_final_fullpath.wstring().c_str(), NULL);
|
||||
|
||||
// Enter rename mode
|
||||
this->template_entry->enter_rename_mode(site_of_folder, target_final_fullpath);
|
||||
|
||||
Trace::EventCopyTemplateResult(S_OK);
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
Trace::EventCopyTemplateResult(S_FALSE);
|
||||
Logger::error(ex.what());
|
||||
|
||||
hr = S_FALSE;
|
||||
}
|
||||
|
||||
trace.Flush();
|
||||
trace.UpdateState(false);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP shell_context_sub_menu_item::GetFlags(_Out_ EXPCMDFLAGS* returned_flags)
|
||||
@@ -120,5 +162,9 @@ IFACEMETHODIMP template_folder_context_menu_item::GetIcon(_In_opt_ IShellItemArr
|
||||
|
||||
IFACEMETHODIMP template_folder_context_menu_item::Invoke(_In_opt_ IShellItemArray* selection, _In_opt_ IBindCtx*) noexcept
|
||||
{
|
||||
return newplus::utilities::open_template_folder(shell_template_folder);
|
||||
Logger::info(L"Open templates folder");
|
||||
const std::wstring verb_hardcoded_do_not_change = L"open";
|
||||
ShellExecute(nullptr, verb_hardcoded_do_not_change.c_str(), shell_template_folder.c_str(), NULL, NULL, SW_SHOWNORMAL);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -11,11 +11,6 @@ template_folder::template_folder(const std::filesystem::path newplus_template_fo
|
||||
this->template_folder_path = newplus_template_folder;
|
||||
}
|
||||
|
||||
template_folder::~template_folder()
|
||||
{
|
||||
list_of_templates.clear();
|
||||
}
|
||||
|
||||
void template_folder::init()
|
||||
{
|
||||
rescan_template_folder();
|
||||
|
||||
@@ -13,8 +13,6 @@ namespace newplus
|
||||
{
|
||||
public:
|
||||
template_folder(const std::filesystem::path newplus_template_folder);
|
||||
~template_folder();
|
||||
|
||||
void rescan_template_folder();
|
||||
|
||||
std::filesystem::path template_folder_path;
|
||||
|
||||
@@ -63,11 +63,6 @@ std::wstring template_item::get_explorer_icon() const
|
||||
return utilities::get_explorer_icon(path);
|
||||
}
|
||||
|
||||
HICON template_item::get_explorer_icon_handle() const
|
||||
{
|
||||
return utilities::get_explorer_icon_handle(path);
|
||||
}
|
||||
|
||||
std::filesystem::path template_item::copy_object_to(const HWND window_handle, const std::filesystem::path destination) const
|
||||
{
|
||||
// SHFILEOPSTRUCT wants the from and to paths to be terminated with two NULLs,
|
||||
@@ -91,14 +86,6 @@ std::filesystem::path template_item::copy_object_to(const HWND window_handle, co
|
||||
if (!file_operation_params.hNameMappings)
|
||||
{
|
||||
// No file name collision on copy
|
||||
if (utilities::is_directory(this->path))
|
||||
{
|
||||
// Append dir for consistency on directory naming inclusion for with and without collision
|
||||
std::filesystem::path with_dir = destination;
|
||||
with_dir /= this->path.filename();
|
||||
return with_dir;
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
@@ -117,23 +104,44 @@ std::filesystem::path template_item::copy_object_to(const HWND window_handle, co
|
||||
return final_path;
|
||||
}
|
||||
|
||||
void template_item::refresh_target(const std::filesystem::path target_final_fullpath) const
|
||||
void template_item::enter_rename_mode(const ComPtr<IUnknown> site, const std::filesystem::path target_fullpath) const
|
||||
{
|
||||
SHChangeNotify(SHCNE_CREATE, SHCNF_PATH | SHCNF_FLUSH, target_final_fullpath.wstring().c_str(), NULL);
|
||||
}
|
||||
|
||||
void template_item::enter_rename_mode(const std::filesystem::path target_fullpath) const
|
||||
{
|
||||
std::thread thread_for_renaming_workaround(rename_on_other_thread_workaround, target_fullpath);
|
||||
std::thread thread_for_renaming_workaround(rename_on_other_thread_workaround, site, target_fullpath);
|
||||
thread_for_renaming_workaround.detach();
|
||||
}
|
||||
|
||||
void template_item::rename_on_other_thread_workaround(const std::filesystem::path target_fullpath)
|
||||
void template_item::rename_on_other_thread_workaround(const ComPtr<IUnknown> site, const std::filesystem::path target_fullpath)
|
||||
{
|
||||
// Have been unable to have Windows Explorer Shell enter rename mode from the main thread
|
||||
// Sleep for a bit to only enter rename mode when icon has been drawn.
|
||||
const std::chrono::milliseconds approx_wait_for_icon_redraw_not_needed{ 50 };
|
||||
// Sleep for a bit to only enter rename mode when icon has been drawn. Not strictly needed.
|
||||
const std::chrono::milliseconds approx_wait_for_icon_redraw_not_needed{ 350 };
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(approx_wait_for_icon_redraw_not_needed));
|
||||
|
||||
newplus::utilities::explorer_enter_rename_mode(target_fullpath);
|
||||
const std::wstring filename = target_fullpath.filename();
|
||||
|
||||
ComPtr<IServiceProvider> service_provider;
|
||||
site->QueryInterface(IID_PPV_ARGS(&service_provider));
|
||||
ComPtr<IFolderView> folder_view;
|
||||
service_provider->QueryService(__uuidof(IFolderView), IID_PPV_ARGS(&folder_view));
|
||||
|
||||
int count = 0;
|
||||
folder_view->ItemCount(SVGIO_ALLVIEW, &count);
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
std::wstring path_of_item(MAX_PATH, 0);
|
||||
LPITEMIDLIST pidl;
|
||||
|
||||
folder_view->Item(i, &pidl);
|
||||
SHGetPathFromIDList(pidl, &path_of_item[0]);
|
||||
CoTaskMemFree(pidl);
|
||||
|
||||
std::wstring current_filename = std::filesystem::path(path_of_item.c_str()).filename();
|
||||
|
||||
if (utilities::wstring_same_when_comparing_ignore_case(filename, current_filename))
|
||||
{
|
||||
folder_view->SelectItem(i, SVSI_EDIT | SVSI_SELECT | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE | SVSI_FOCUSED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,19 +20,15 @@ namespace newplus
|
||||
std::wstring get_target_filename(const bool include_starting_digits) const;
|
||||
|
||||
std::wstring get_explorer_icon() const;
|
||||
|
||||
HICON get_explorer_icon_handle() const;
|
||||
|
||||
std::filesystem::path copy_object_to(const HWND window_handle, const std::filesystem::path destination) const;
|
||||
|
||||
void refresh_target(const std::filesystem::path target_final_fullpath) const;
|
||||
|
||||
void enter_rename_mode(const std::filesystem::path target_fullpath) const;
|
||||
void enter_rename_mode(const ComPtr<IUnknown> site, const std::filesystem::path target_folder) const;
|
||||
|
||||
std::filesystem::path path;
|
||||
|
||||
private:
|
||||
static void rename_on_other_thread_workaround(const std::filesystem::path target_fullpath);
|
||||
static void rename_on_other_thread_workaround(const ComPtr<IUnknown> site, const std::filesystem::path target_fullpath);
|
||||
|
||||
std::wstring remove_starting_digits_from_filename(std::wstring filename) const;
|
||||
};
|
||||
|
||||
@@ -58,12 +58,3 @@ void Trace::EventCopyTemplateResult(_In_ const HRESULT hr) noexcept
|
||||
TraceLoggingHResult(hr),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
}
|
||||
|
||||
void Trace::EventOpenTemplates() noexcept
|
||||
{
|
||||
TraceLoggingWriteWrapper(
|
||||
g_hProvider,
|
||||
"NewPlus_EventOpenTemplates",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
}
|
||||
|
||||
@@ -12,5 +12,4 @@ public:
|
||||
static void EventShowTemplateItems(_In_ const size_t number_of_templates) noexcept;
|
||||
static void EventCopyTemplate(_In_ const std::wstring template_file_extension) noexcept;
|
||||
static void EventCopyTemplateResult(_In_ const HRESULT hr) noexcept;
|
||||
static void EventOpenTemplates() noexcept;
|
||||
};
|
||||
|
||||
@@ -1,217 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Windows.Management.Deployment;
|
||||
|
||||
namespace WorkspacesCsharpLibrary.Models
|
||||
{
|
||||
public class BaseApplication : INotifyPropertyChanged, IDisposable
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, e);
|
||||
}
|
||||
|
||||
public string PwaAppId { get; set; }
|
||||
|
||||
public string AppPath { get; set; }
|
||||
|
||||
private bool _isNotFound;
|
||||
|
||||
public string PackagedId { get; set; }
|
||||
|
||||
public string PackagedName { get; set; }
|
||||
|
||||
public string PackagedPublisherID { get; set; }
|
||||
|
||||
public string Aumid { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsNotFound
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isNotFound;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_isNotFound != value)
|
||||
{
|
||||
_isNotFound = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsNotFound)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Icon _icon;
|
||||
|
||||
public Icon Icon
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_icon == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (IsPackagedApp)
|
||||
{
|
||||
Uri uri = GetAppLogoByPackageFamilyName();
|
||||
var bitmap = new Bitmap(uri.LocalPath);
|
||||
var iconHandle = bitmap.GetHicon();
|
||||
_icon = Icon.FromHandle(iconHandle);
|
||||
}
|
||||
else if (IsEdge || IsChrome)
|
||||
{
|
||||
string iconFilename = PwaHelper.GetPwaIconFilename(PwaAppId);
|
||||
if (!string.IsNullOrEmpty(iconFilename))
|
||||
{
|
||||
Bitmap bitmap;
|
||||
if (iconFilename.EndsWith("ico", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
bitmap = new Bitmap(iconFilename);
|
||||
}
|
||||
else
|
||||
{
|
||||
bitmap = (Bitmap)Image.FromFile(iconFilename);
|
||||
}
|
||||
|
||||
var iconHandle = bitmap.GetHicon();
|
||||
_icon = Icon.FromHandle(iconHandle);
|
||||
}
|
||||
}
|
||||
|
||||
if (_icon == null)
|
||||
{
|
||||
_icon = Icon.ExtractAssociatedIcon(AppPath);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
IsNotFound = true;
|
||||
_icon = new Icon(@"Assets\Workspaces\DefaultIcon.ico");
|
||||
}
|
||||
}
|
||||
|
||||
return _icon;
|
||||
}
|
||||
}
|
||||
|
||||
private BitmapImage _iconBitmapImage;
|
||||
|
||||
public BitmapImage IconBitmapImage
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_iconBitmapImage == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Bitmap previewBitmap = new Bitmap(32, 32);
|
||||
using (Graphics graphics = Graphics.FromImage(previewBitmap))
|
||||
{
|
||||
graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
|
||||
graphics.DrawIcon(Icon, new Rectangle(0, 0, 32, 32));
|
||||
}
|
||||
|
||||
using (var memory = new MemoryStream())
|
||||
{
|
||||
previewBitmap.Save(memory, ImageFormat.Png);
|
||||
memory.Position = 0;
|
||||
|
||||
BitmapImage bitmapImage = new BitmapImage();
|
||||
bitmapImage.BeginInit();
|
||||
bitmapImage.StreamSource = memory;
|
||||
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
|
||||
bitmapImage.EndInit();
|
||||
bitmapImage.Freeze();
|
||||
|
||||
_iconBitmapImage = bitmapImage;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
return _iconBitmapImage;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEdge
|
||||
{
|
||||
get => AppPath.EndsWith("edge.exe", StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
public bool IsChrome
|
||||
{
|
||||
get => AppPath.EndsWith("chrome.exe", StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
public Uri GetAppLogoByPackageFamilyName()
|
||||
{
|
||||
var pkgManager = new PackageManager();
|
||||
var pkg = pkgManager.FindPackagesForUser(string.Empty, PackagedId).FirstOrDefault();
|
||||
|
||||
if (pkg == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return pkg.Logo;
|
||||
}
|
||||
|
||||
private bool? _isPackagedApp;
|
||||
|
||||
public bool IsPackagedApp
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_isPackagedApp == null)
|
||||
{
|
||||
if (!AppPath.StartsWith("C:\\Program Files\\WindowsApps\\", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
_isPackagedApp = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
string appPath = AppPath.Replace("C:\\Program Files\\WindowsApps\\", string.Empty);
|
||||
Regex packagedAppPathRegex = new Regex(@"(?<APPID>[^_]*)_\d+.\d+.\d+.\d+_x64__(?<PublisherID>[^\\]*)", RegexOptions.ExplicitCapture | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
|
||||
Match match = packagedAppPathRegex.Match(appPath);
|
||||
_isPackagedApp = match.Success;
|
||||
if (match.Success)
|
||||
{
|
||||
PackagedName = match.Groups["APPID"].Value;
|
||||
PackagedPublisherID = match.Groups["PublisherID"].Value;
|
||||
PackagedId = $"{PackagedName}_{PackagedPublisherID}";
|
||||
Aumid = $"{PackagedId}!App";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _isPackagedApp.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +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.
|
||||
|
||||
namespace WorkspacesCsharpLibrary
|
||||
{
|
||||
public class PwaApp
|
||||
{
|
||||
public required string Name { get; set; }
|
||||
|
||||
public required string IconFilename { get; set; }
|
||||
|
||||
public required string AppId { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace WorkspacesCsharpLibrary
|
||||
{
|
||||
public class PwaHelper
|
||||
{
|
||||
private const string ChromeBase = "Google\\Chrome\\User Data\\Default\\Web Applications";
|
||||
private const string EdgeBase = "Microsoft\\Edge\\User Data\\Default\\Web Applications";
|
||||
private const string ResourcesDir = "Manifest Resources";
|
||||
private const string IconsDir = "Icons";
|
||||
private const string PwaDirIdentifier = "_CRX_";
|
||||
|
||||
private static List<PwaApp> pwaApps = new List<PwaApp>();
|
||||
|
||||
public PwaHelper()
|
||||
{
|
||||
InitPwaData(EdgeBase);
|
||||
InitPwaData(ChromeBase);
|
||||
}
|
||||
|
||||
private void InitPwaData(string p_baseDir)
|
||||
{
|
||||
var baseFolderName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), p_baseDir);
|
||||
if (Directory.Exists(baseFolderName))
|
||||
{
|
||||
foreach (string subDir in Directory.GetDirectories(baseFolderName))
|
||||
{
|
||||
string dirName = Path.GetFileName(subDir);
|
||||
if (!dirName.StartsWith(PwaDirIdentifier, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string appId = dirName.Substring(PwaDirIdentifier.Length, dirName.Length - PwaDirIdentifier.Length).Trim('_');
|
||||
|
||||
foreach (string iconFile in Directory.GetFiles(subDir, "*.ico"))
|
||||
{
|
||||
string filenameWithoutExtension = Path.GetFileNameWithoutExtension(iconFile);
|
||||
|
||||
pwaApps.Add(new PwaApp() { Name = filenameWithoutExtension, IconFilename = iconFile, AppId = appId });
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
string resourcesDir = Path.Combine(baseFolderName, ResourcesDir);
|
||||
if (Directory.Exists(resourcesDir))
|
||||
{
|
||||
foreach (string subDir in Directory.GetDirectories(resourcesDir))
|
||||
{
|
||||
string dirName = Path.GetFileName(subDir);
|
||||
if (pwaApps.Any(app => app.AppId == dirName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
string iconsDir = Path.Combine(subDir, IconsDir);
|
||||
if (Directory.Exists(iconsDir))
|
||||
{
|
||||
foreach (string iconFile in Directory.GetFiles(iconsDir, "*.png"))
|
||||
{
|
||||
string filenameWithoutExtension = Path.GetFileNameWithoutExtension(iconFile);
|
||||
|
||||
pwaApps.Add(new PwaApp() { Name = filenameWithoutExtension, IconFilename = iconFile, AppId = dirName });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetPwaIconFilename(string pwaAppId)
|
||||
{
|
||||
var candidates = pwaApps.Where(x => x.AppId == pwaAppId).ToList();
|
||||
if (candidates.Count > 0)
|
||||
{
|
||||
return candidates.First().IconFilename;
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<Import Project="..\..\..\Common.Dotnet.CsWinRT.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<AssemblyTitle>PowerToys.WorkspacesCsharpLibrary</AssemblyTitle>
|
||||
<AssemblyDescription>PowerToys Workspaces Csharp Library</AssemblyDescription>
|
||||
<Description>PowerToys Workspaces Csharp Library</Description>
|
||||
<UseWPF>true</UseWPF>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
|
||||
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
|
||||
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)</OutputPath>
|
||||
<AssemblyName>PowerToys.WorkspacesCsharpLibrary</AssemblyName>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -37,8 +37,6 @@ namespace WorkspacesEditor.Data
|
||||
|
||||
public string AppUserModelId { get; set; }
|
||||
|
||||
public string PwaAppId { get; set; }
|
||||
|
||||
public string CommandLineArguments { get; set; }
|
||||
|
||||
public bool IsElevated { get; set; }
|
||||
|
||||
@@ -13,17 +13,18 @@ using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
using ManagedCommon;
|
||||
using Windows.Management.Deployment;
|
||||
using WorkspacesCsharpLibrary;
|
||||
using WorkspacesCsharpLibrary.Models;
|
||||
|
||||
namespace WorkspacesEditor.Models
|
||||
{
|
||||
public class Application : BaseApplication, IDisposable
|
||||
public class Application : INotifyPropertyChanged, IDisposable
|
||||
{
|
||||
private bool _isInitialized;
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public Application()
|
||||
{
|
||||
}
|
||||
@@ -36,7 +37,6 @@ namespace WorkspacesEditor.Models
|
||||
AppTitle = other.AppTitle;
|
||||
PackageFullName = other.PackageFullName;
|
||||
AppUserModelId = other.AppUserModelId;
|
||||
PwaAppId = other.PwaAppId;
|
||||
CommandLineArguments = other.CommandLineArguments;
|
||||
IsElevated = other.IsElevated;
|
||||
CanLaunchElevated = other.CanLaunchElevated;
|
||||
@@ -100,6 +100,8 @@ namespace WorkspacesEditor.Models
|
||||
|
||||
public string AppName { get; set; }
|
||||
|
||||
public string AppPath { get; set; }
|
||||
|
||||
public string AppTitle { get; set; }
|
||||
|
||||
public string PackageFullName { get; set; }
|
||||
@@ -185,6 +187,26 @@ namespace WorkspacesEditor.Models
|
||||
|
||||
public bool IsAppMainParamVisible { get => !string.IsNullOrWhiteSpace(_appMainParams); }
|
||||
|
||||
private bool _isNotFound;
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsNotFound
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isNotFound;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_isNotFound != value)
|
||||
{
|
||||
_isNotFound = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsNotFound)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsHighlighted { get; set; }
|
||||
|
||||
@@ -200,6 +222,100 @@ namespace WorkspacesEditor.Models
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
private Icon _icon = null;
|
||||
|
||||
[JsonIgnore]
|
||||
public Icon Icon
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_icon == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (IsPackagedApp)
|
||||
{
|
||||
Uri uri = GetAppLogoByPackageFamilyName();
|
||||
var bitmap = new Bitmap(uri.LocalPath);
|
||||
var iconHandle = bitmap.GetHicon();
|
||||
_icon = Icon.FromHandle(iconHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
_icon = Icon.ExtractAssociatedIcon(AppPath);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Logger.LogWarning($"Icon not found on app path: {AppPath}. Using default icon");
|
||||
IsNotFound = true;
|
||||
_icon = new Icon(@"Assets\Workspaces\DefaultIcon.ico");
|
||||
}
|
||||
}
|
||||
|
||||
return _icon;
|
||||
}
|
||||
}
|
||||
|
||||
public Uri GetAppLogoByPackageFamilyName()
|
||||
{
|
||||
var pkgManager = new PackageManager();
|
||||
var pkg = pkgManager.FindPackagesForUser(string.Empty, PackagedId).FirstOrDefault();
|
||||
|
||||
if (pkg == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return pkg.Logo;
|
||||
}
|
||||
|
||||
private BitmapImage _iconBitmapImage;
|
||||
|
||||
public BitmapImage IconBitmapImage
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_iconBitmapImage == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Bitmap previewBitmap = new Bitmap(32, 32);
|
||||
using (Graphics graphics = Graphics.FromImage(previewBitmap))
|
||||
{
|
||||
graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
|
||||
graphics.DrawIcon(Icon, new Rectangle(0, 0, 32, 32));
|
||||
}
|
||||
|
||||
using (var memory = new MemoryStream())
|
||||
{
|
||||
previewBitmap.Save(memory, ImageFormat.Png);
|
||||
memory.Position = 0;
|
||||
|
||||
var bitmapImage = new BitmapImage();
|
||||
bitmapImage.BeginInit();
|
||||
bitmapImage.StreamSource = memory;
|
||||
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
|
||||
bitmapImage.EndInit();
|
||||
bitmapImage.Freeze();
|
||||
|
||||
_iconBitmapImage = bitmapImage;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError($"Exception while drawing icon for app with path: {AppPath}. Exception message: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return _iconBitmapImage;
|
||||
}
|
||||
}
|
||||
|
||||
private WindowPosition _position;
|
||||
|
||||
public WindowPosition Position
|
||||
@@ -251,11 +367,56 @@ namespace WorkspacesEditor.Models
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, e);
|
||||
}
|
||||
|
||||
public void InitializationFinished()
|
||||
{
|
||||
_isInitialized = true;
|
||||
}
|
||||
|
||||
private bool? _isPackagedApp;
|
||||
|
||||
public string PackagedId { get; set; }
|
||||
|
||||
public string PackagedName { get; set; }
|
||||
|
||||
public string PackagedPublisherID { get; set; }
|
||||
|
||||
public string Aumid { get; set; }
|
||||
|
||||
public bool IsPackagedApp
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_isPackagedApp == null)
|
||||
{
|
||||
if (!AppPath.StartsWith("C:\\Program Files\\WindowsApps\\", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
_isPackagedApp = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
string appPath = AppPath.Replace("C:\\Program Files\\WindowsApps\\", string.Empty);
|
||||
Regex packagedAppPathRegex = new Regex(@"(?<APPID>[^_]*)_\d+.\d+.\d+.\d+_x64__(?<PublisherID>[^\\]*)", RegexOptions.ExplicitCapture | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
|
||||
Match match = packagedAppPathRegex.Match(appPath);
|
||||
_isPackagedApp = match.Success;
|
||||
if (match.Success)
|
||||
{
|
||||
PackagedName = match.Groups["APPID"].Value;
|
||||
PackagedPublisherID = match.Groups["PublisherID"].Value;
|
||||
PackagedId = $"{PackagedName}_{PackagedPublisherID}";
|
||||
Aumid = $"{PackagedId}!App";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _isPackagedApp.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private bool _isExpanded;
|
||||
|
||||
public bool IsExpanded
|
||||
@@ -293,6 +454,11 @@ namespace WorkspacesEditor.Models
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
internal void CommandLineTextChanged(string newCommandLineValue)
|
||||
{
|
||||
CommandLineArguments = newCommandLineValue;
|
||||
|
||||
@@ -251,11 +251,10 @@ namespace WorkspacesEditor.Models
|
||||
{
|
||||
Models.Application newApp = new Models.Application()
|
||||
{
|
||||
Id = string.IsNullOrEmpty(app.Id) ? $"{{{Guid.NewGuid().ToString()}}}" : app.Id,
|
||||
Id = app.Id != null ? app.Id : $"{{{Guid.NewGuid().ToString()}}}",
|
||||
AppName = app.Application,
|
||||
AppPath = app.ApplicationPath,
|
||||
AppTitle = app.Title,
|
||||
PwaAppId = string.IsNullOrEmpty(app.PwaAppId) ? string.Empty : app.PwaAppId,
|
||||
PackageFullName = app.PackageFullName,
|
||||
AppUserModelId = app.AppUserModelId,
|
||||
Parent = this,
|
||||
|
||||
@@ -75,16 +75,16 @@ namespace WorkspacesEditor.Utils
|
||||
|
||||
foreach (Application app in appsIncluded)
|
||||
{
|
||||
if (repeatCounter.TryGetValue(app.AppPath + app.AppTitle, out int value))
|
||||
if (repeatCounter.TryGetValue(app.AppPath, out int value))
|
||||
{
|
||||
repeatCounter[app.AppPath + app.AppTitle] = ++value;
|
||||
repeatCounter[app.AppPath] = ++value;
|
||||
}
|
||||
else
|
||||
{
|
||||
repeatCounter.Add(app.AppPath + app.AppTitle, 1);
|
||||
repeatCounter.Add(app.AppPath, 1);
|
||||
}
|
||||
|
||||
app.RepeatIndex = repeatCounter[app.AppPath + app.AppTitle];
|
||||
app.RepeatIndex = repeatCounter[app.AppPath];
|
||||
}
|
||||
|
||||
foreach (Application app in project.Applications.Where(x => !x.IsIncluded))
|
||||
|
||||
@@ -103,7 +103,6 @@ namespace WorkspacesEditor.Utils
|
||||
Title = app.AppTitle,
|
||||
PackageFullName = app.PackageFullName,
|
||||
AppUserModelId = app.AppUserModelId,
|
||||
PwaAppId = app.PwaAppId,
|
||||
CommandLineArguments = app.CommandLineArguments,
|
||||
IsElevated = app.IsElevated,
|
||||
CanLaunchElevated = app.CanLaunchElevated,
|
||||
|
||||
@@ -14,10 +14,10 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using System.Windows;
|
||||
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using WorkspacesCsharpLibrary;
|
||||
using WorkspacesEditor.Data;
|
||||
using WorkspacesEditor.Models;
|
||||
using WorkspacesEditor.Telemetry;
|
||||
@@ -39,7 +39,6 @@ namespace WorkspacesEditor.ViewModels
|
||||
private MainWindow _mainWindow;
|
||||
private Timer lastUpdatedTimer;
|
||||
private WorkspacesSettings settings;
|
||||
private PwaHelper _pwaHelper;
|
||||
|
||||
public ObservableCollection<Project> Workspaces { get; set; } = new ObservableCollection<Project>();
|
||||
|
||||
@@ -148,7 +147,6 @@ namespace WorkspacesEditor.ViewModels
|
||||
settings = Utils.Settings.ReadSettings();
|
||||
_orderByIndex = (int)settings.Properties.SortBy;
|
||||
_workspacesEditorIO = workspacesEditorIO;
|
||||
_pwaHelper = new PwaHelper();
|
||||
lastUpdatedTimer = new System.Timers.Timer();
|
||||
lastUpdatedTimer.Interval = 1000;
|
||||
lastUpdatedTimer.Elapsed += LastUpdatedTimerElapsed;
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ControlzEx" />
|
||||
<PackageReference Include="ModernWpfUI" />
|
||||
<PackageReference Include="System.IO.Abstractions" />
|
||||
</ItemGroup>
|
||||
@@ -76,7 +77,6 @@
|
||||
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
|
||||
<ProjectReference Include="..\..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||
<ProjectReference Include="..\WorkspacesCsharpLibrary\WorkspacesCsharpLibrary.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
Margin="10"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Source="{Binding IconBitmapImage, UpdateSourceTrigger=PropertyChanged}" />
|
||||
Source="{Binding IconBitmapImage}" />
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
Width="20"
|
||||
|
||||
@@ -18,15 +18,6 @@ using namespace Windows::Management::Deployment;
|
||||
|
||||
namespace AppLauncher
|
||||
{
|
||||
namespace NonLocalizable
|
||||
{
|
||||
const std::wstring EdgeFilename = L"msedge.exe";
|
||||
const std::wstring EdgePwaFilename = L"msedge_proxy.exe";
|
||||
const std::wstring ChromeFilename = L"chrome.exe";
|
||||
const std::wstring ChromePwaFilename = L"chrome_proxy.exe";
|
||||
const std::wstring PwaCommandLineAddition = L"--profile-directory=Default --app-id=";
|
||||
}
|
||||
|
||||
Result<SHELLEXECUTEINFO, std::wstring> LaunchApp(const std::wstring& appPath, const std::wstring& commandLineArgs, bool elevated)
|
||||
{
|
||||
std::wstring dir = std::filesystem::path(appPath).parent_path();
|
||||
@@ -142,50 +133,30 @@ namespace AppLauncher
|
||||
launched = LaunchPackagedApp(app.packageFullName, launchErrors);
|
||||
}
|
||||
|
||||
std::wstring appPathFinal;
|
||||
std::wstring commandLineArgsFinal;
|
||||
appPathFinal = app.path;
|
||||
commandLineArgsFinal = app.commandLineArgs;
|
||||
|
||||
if (!launched && !app.pwaAppId.empty())
|
||||
{
|
||||
std::filesystem::path appPath(app.path);
|
||||
if (appPath.filename() == NonLocalizable::EdgeFilename)
|
||||
{
|
||||
appPathFinal = appPath.parent_path() / NonLocalizable::EdgePwaFilename;
|
||||
commandLineArgsFinal = NonLocalizable::PwaCommandLineAddition + app.pwaAppId + L" " + app.commandLineArgs;
|
||||
}
|
||||
if (appPath.filename() == NonLocalizable::ChromeFilename)
|
||||
{
|
||||
appPathFinal = appPath.parent_path() / NonLocalizable::ChromePwaFilename;
|
||||
commandLineArgsFinal = NonLocalizable::PwaCommandLineAddition + app.pwaAppId + L" " + app.commandLineArgs;
|
||||
}
|
||||
}
|
||||
|
||||
if (!launched)
|
||||
{
|
||||
Logger::trace(L"Launching {} at {}", app.name, appPathFinal);
|
||||
Logger::trace(L"Launching {} at {}", app.name, app.path);
|
||||
|
||||
DWORD dwAttrib = GetFileAttributesW(appPathFinal.c_str());
|
||||
DWORD dwAttrib = GetFileAttributesW(app.path.c_str());
|
||||
if (dwAttrib == INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
Logger::error(L"File not found at {}", appPathFinal);
|
||||
launchErrors.push_back({ std::filesystem::path(appPathFinal).filename(), L"File not found" });
|
||||
Logger::error(L"File not found at {}", app.path);
|
||||
launchErrors.push_back({ std::filesystem::path(app.path).filename(), L"File not found" });
|
||||
return false;
|
||||
}
|
||||
|
||||
auto res = LaunchApp(appPathFinal, commandLineArgsFinal, app.isElevated);
|
||||
auto res = LaunchApp(app.path, app.commandLineArgs, app.isElevated);
|
||||
if (res.isOk())
|
||||
{
|
||||
launched = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
launchErrors.push_back({ std::filesystem::path(appPathFinal).filename(), res.error() });
|
||||
launchErrors.push_back({ std::filesystem::path(app.path).filename(), res.error() });
|
||||
}
|
||||
}
|
||||
|
||||
Logger::trace(L"{} {} at {}", app.name, (launched ? L"launched" : L"not launched"), appPathFinal);
|
||||
Logger::trace(L"{} {} at {}", app.name, (launched ? L"launched" : L"not launched"), app.path);
|
||||
return launched;
|
||||
}
|
||||
}
|
||||
@@ -16,8 +16,6 @@ namespace WorkspacesLauncherUI.Data
|
||||
|
||||
public string AppUserModelId { get; set; }
|
||||
|
||||
public string PwaAppId { get; set; }
|
||||
|
||||
public string CommandLineArguments { get; set; }
|
||||
|
||||
public bool IsElevated { get; set; }
|
||||
|
||||
@@ -3,23 +3,77 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
using ManagedCommon;
|
||||
using WorkspacesCsharpLibrary.Models;
|
||||
using Windows.Management.Deployment;
|
||||
using WorkspacesLauncherUI.Data;
|
||||
|
||||
namespace WorkspacesLauncherUI.Models
|
||||
{
|
||||
public class AppLaunching : BaseApplication, IDisposable
|
||||
public class AppLaunching : INotifyPropertyChanged, IDisposable
|
||||
{
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
public void OnPropertyChanged(PropertyChangedEventArgs e)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, e);
|
||||
}
|
||||
|
||||
public ApplicationWrapper Application { get; set; }
|
||||
|
||||
public bool Loading => LaunchState == LaunchingState.Waiting || LaunchState == LaunchingState.Launched;
|
||||
|
||||
public string Name { get; set; }
|
||||
private Icon _icon;
|
||||
|
||||
public Icon Icon
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_icon == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (IsPackagedApp)
|
||||
{
|
||||
Uri uri = GetAppLogoByPackageFamilyName();
|
||||
var bitmap = new Bitmap(uri.LocalPath);
|
||||
var iconHandle = bitmap.GetHicon();
|
||||
_icon = Icon.FromHandle(iconHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
_icon = Icon.ExtractAssociatedIcon(Application.ApplicationPath);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Logger.LogWarning($"Icon not found on app path: {Application.ApplicationPath}. Using default icon");
|
||||
IsNotFound = true;
|
||||
_icon = new Icon(@"Assets\Workspaces\DefaultIcon.ico");
|
||||
}
|
||||
}
|
||||
|
||||
return _icon;
|
||||
}
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return Application.Application;
|
||||
}
|
||||
}
|
||||
|
||||
public LaunchingState LaunchState { get; set; }
|
||||
|
||||
@@ -42,5 +96,128 @@ namespace WorkspacesLauncherUI.Models
|
||||
_ => new SolidColorBrush(System.Windows.Media.Color.FromArgb(255, 254, 0, 0)),
|
||||
};
|
||||
}
|
||||
|
||||
private bool _isNotFound;
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsNotFound
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isNotFound;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_isNotFound != value)
|
||||
{
|
||||
_isNotFound = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsNotFound)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Uri GetAppLogoByPackageFamilyName()
|
||||
{
|
||||
var pkgManager = new PackageManager();
|
||||
var pkg = pkgManager.FindPackagesForUser(string.Empty, PackagedId).FirstOrDefault();
|
||||
|
||||
if (pkg == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return pkg.Logo;
|
||||
}
|
||||
|
||||
private bool? _isPackagedApp;
|
||||
|
||||
public string PackagedId { get; set; }
|
||||
|
||||
public string PackagedName { get; set; }
|
||||
|
||||
public string PackagedPublisherID { get; set; }
|
||||
|
||||
public string Aumid { get; set; }
|
||||
|
||||
public bool IsPackagedApp
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_isPackagedApp == null)
|
||||
{
|
||||
if (!Application.ApplicationPath.StartsWith("C:\\Program Files\\WindowsApps\\", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
_isPackagedApp = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
string appPath = Application.ApplicationPath.Replace("C:\\Program Files\\WindowsApps\\", string.Empty);
|
||||
Regex packagedAppPathRegex = new Regex(@"(?<APPID>[^_]*)_\d+.\d+.\d+.\d+_x64__(?<PublisherID>[^\\]*)", RegexOptions.ExplicitCapture | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
|
||||
Match match = packagedAppPathRegex.Match(appPath);
|
||||
_isPackagedApp = match.Success;
|
||||
if (match.Success)
|
||||
{
|
||||
PackagedName = match.Groups["APPID"].Value;
|
||||
PackagedPublisherID = match.Groups["PublisherID"].Value;
|
||||
PackagedId = $"{PackagedName}_{PackagedPublisherID}";
|
||||
Aumid = $"{PackagedId}!App";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _isPackagedApp.Value;
|
||||
}
|
||||
}
|
||||
|
||||
private BitmapImage _iconBitmapImage;
|
||||
|
||||
public BitmapImage IconBitmapImage
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_iconBitmapImage == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Bitmap previewBitmap = new Bitmap(32, 32);
|
||||
using (Graphics graphics = Graphics.FromImage(previewBitmap))
|
||||
{
|
||||
graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
|
||||
graphics.DrawIcon(Icon, new Rectangle(0, 0, 32, 32));
|
||||
}
|
||||
|
||||
using (var memory = new MemoryStream())
|
||||
{
|
||||
previewBitmap.Save(memory, ImageFormat.Png);
|
||||
memory.Position = 0;
|
||||
|
||||
var bitmapImage = new BitmapImage();
|
||||
bitmapImage.BeginInit();
|
||||
bitmapImage.StreamSource = memory;
|
||||
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
|
||||
bitmapImage.EndInit();
|
||||
bitmapImage.Freeze();
|
||||
|
||||
_iconBitmapImage = bitmapImage;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogError($"Exception while drawing icon for app with path: {Application.ApplicationPath}. Exception message: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return _iconBitmapImage;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,8 @@ using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
|
||||
using ManagedCommon;
|
||||
using WorkspacesCsharpLibrary;
|
||||
using WorkspacesLauncherUI.Data;
|
||||
using WorkspacesLauncherUI.Models;
|
||||
using WorkspacesLauncherUI.Utils;
|
||||
|
||||
namespace WorkspacesLauncherUI.ViewModels
|
||||
{
|
||||
@@ -22,7 +20,6 @@ namespace WorkspacesLauncherUI.ViewModels
|
||||
|
||||
private StatusWindow _snapshotWindow;
|
||||
private int launcherProcessID;
|
||||
private PwaHelper _pwaHelper;
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
@@ -33,8 +30,6 @@ namespace WorkspacesLauncherUI.ViewModels
|
||||
|
||||
public MainViewModel()
|
||||
{
|
||||
_pwaHelper = new PwaHelper();
|
||||
|
||||
// receive IPC Message
|
||||
App.IPCMessageReceivedCallback = (string msg) =>
|
||||
{
|
||||
@@ -59,11 +54,7 @@ namespace WorkspacesLauncherUI.ViewModels
|
||||
{
|
||||
appLaunchingList.Add(new AppLaunching()
|
||||
{
|
||||
Name = app.Application.Application,
|
||||
AppPath = app.Application.ApplicationPath,
|
||||
PackagedName = app.Application.PackageFullName,
|
||||
Aumid = app.Application.AppUserModelId,
|
||||
PwaAppId = app.Application.PwaAppId,
|
||||
Application = app.Application,
|
||||
LaunchState = app.State,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -79,7 +79,6 @@
|
||||
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
|
||||
<ProjectReference Include="..\..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
|
||||
<ProjectReference Include="..\..\..\settings-ui\Settings.UI.Library\Settings.UI.Library.csproj" />
|
||||
<ProjectReference Include="..\WorkspacesCsharpLibrary\WorkspacesCsharpLibrary.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace WorkspacesData
|
||||
std::wstring settingsFolderPath = PTSettingsHelper::get_module_save_folder_location(NonLocalizable::ModuleKey);
|
||||
return settingsFolderPath + L"\\temp-workspaces.json";
|
||||
}
|
||||
|
||||
|
||||
RECT WorkspacesProject::Application::Position::toRect() const noexcept
|
||||
{
|
||||
return RECT{ .left = x, .top = y, .right = x + width, .bottom = y + height };
|
||||
@@ -79,7 +79,6 @@ namespace WorkspacesData
|
||||
const static wchar_t* AppPathID = L"application-path";
|
||||
const static wchar_t* AppPackageFullNameID = L"package-full-name";
|
||||
const static wchar_t* AppUserModelId = L"app-user-model-id";
|
||||
const static wchar_t* PwaAppId = L"pwa-app-id";
|
||||
const static wchar_t* AppTitleID = L"title";
|
||||
const static wchar_t* CommandLineArgsID = L"command-line-arguments";
|
||||
const static wchar_t* ElevatedID = L"is-elevated";
|
||||
@@ -99,7 +98,6 @@ namespace WorkspacesData
|
||||
json.SetNamedValue(NonLocalizable::AppTitleID, json::value(data.title));
|
||||
json.SetNamedValue(NonLocalizable::AppPackageFullNameID, json::value(data.packageFullName));
|
||||
json.SetNamedValue(NonLocalizable::AppUserModelId, json::value(data.appUserModelId));
|
||||
json.SetNamedValue(NonLocalizable::PwaAppId, json::value(data.pwaAppId));
|
||||
json.SetNamedValue(NonLocalizable::CommandLineArgsID, json::value(data.commandLineArgs));
|
||||
json.SetNamedValue(NonLocalizable::ElevatedID, json::value(data.isElevated));
|
||||
json.SetNamedValue(NonLocalizable::CanLaunchElevatedID, json::value(data.canLaunchElevated));
|
||||
@@ -138,11 +136,6 @@ namespace WorkspacesData
|
||||
result.appUserModelId = json.GetNamedString(NonLocalizable::AppUserModelId);
|
||||
}
|
||||
|
||||
if (json.HasKey(NonLocalizable::PwaAppId))
|
||||
{
|
||||
result.pwaAppId = json.GetNamedString(NonLocalizable::PwaAppId);
|
||||
}
|
||||
|
||||
result.commandLineArgs = json.GetNamedString(NonLocalizable::CommandLineArgsID);
|
||||
|
||||
if (json.HasKey(NonLocalizable::ElevatedID))
|
||||
@@ -337,11 +330,11 @@ namespace WorkspacesData
|
||||
{
|
||||
result.isShortcutNeeded = json.GetNamedBoolean(NonLocalizable::IsShortcutNeededID);
|
||||
}
|
||||
|
||||
|
||||
if (json.HasKey(NonLocalizable::MoveExistingWindowsID))
|
||||
{
|
||||
result.moveExistingWindows = json.GetNamedBoolean(NonLocalizable::MoveExistingWindowsID);
|
||||
}
|
||||
result.moveExistingWindows = json.GetNamedBoolean(NonLocalizable::MoveExistingWindowsID);
|
||||
}
|
||||
|
||||
auto appsArray = json.GetNamedArray(NonLocalizable::AppsID);
|
||||
for (uint32_t i = 0; i < appsArray.Size(); ++i)
|
||||
|
||||
@@ -31,7 +31,6 @@ namespace WorkspacesData
|
||||
std::wstring path;
|
||||
std::wstring packageFullName;
|
||||
std::wstring appUserModelId;
|
||||
std::wstring pwaAppId;
|
||||
std::wstring commandLineArgs;
|
||||
bool isElevated{};
|
||||
bool canLaunchElevated{};
|
||||
|
||||
@@ -1,409 +0,0 @@
|
||||
#include "pch.h"
|
||||
#include "PwaHelper.h"
|
||||
#include <ShlObj.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <winternl.h>
|
||||
#include <initguid.h>
|
||||
#include <filesystem>
|
||||
#include <wil/result_macros.h>
|
||||
#include <common/logger/logger.h>
|
||||
#include <common/utils/winapi_error.h>
|
||||
#include <wil\com.h>
|
||||
#pragma comment(lib, "ntdll.lib")
|
||||
|
||||
namespace SnapshotUtils
|
||||
{
|
||||
namespace NonLocalizable
|
||||
{
|
||||
const std::wstring EdgeAppIdIdentifier = L"--app-id=";
|
||||
const std::wstring ChromeAppIdIdentifier = L"Chrome._crx_";
|
||||
const std::wstring ChromeBase = L"Google\\Chrome\\User Data\\Default\\Web Applications";
|
||||
const std::wstring EdgeBase = L"Microsoft\\Edge\\User Data\\Default\\Web Applications";
|
||||
const std::wstring ChromeDirPrefix = L"_crx_";
|
||||
const std::wstring EdgeDirPrefix = L"_crx__";
|
||||
}
|
||||
// {c8900b66-a973-584b-8cae-355b7f55341b}
|
||||
DEFINE_GUID(CLSID_StartMenuCacheAndAppResolver, 0x660b90c8, 0x73a9, 0x4b58, 0x8c, 0xae, 0x35, 0x5b, 0x7f, 0x55, 0x34, 0x1b);
|
||||
|
||||
// {46a6eeff-908e-4dc6-92a6-64be9177b41c}
|
||||
DEFINE_GUID(IID_IAppResolver_7, 0x46a6eeff, 0x908e, 0x4dc6, 0x92, 0xa6, 0x64, 0xbe, 0x91, 0x77, 0xb4, 0x1c);
|
||||
|
||||
// {de25675a-72de-44b4-9373-05170450c140}
|
||||
DEFINE_GUID(IID_IAppResolver_8, 0xde25675a, 0x72de, 0x44b4, 0x93, 0x73, 0x05, 0x17, 0x04, 0x50, 0xc1, 0x40);
|
||||
|
||||
struct IAppResolver_7 : public IUnknown
|
||||
{
|
||||
public:
|
||||
virtual HRESULT STDMETHODCALLTYPE GetAppIDForShortcut() = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE GetAppIDForWindow(HWND hWnd, WCHAR** pszAppId, void* pUnknown1, void* pUnknown2, void* pUnknown3) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE GetAppIDForProcess(DWORD dwProcessId, WCHAR** pszAppId, void* pUnknown1, void* pUnknown2, void* pUnknown3) = 0;
|
||||
};
|
||||
|
||||
struct IAppResolver_8 : public IUnknown
|
||||
{
|
||||
public:
|
||||
virtual HRESULT STDMETHODCALLTYPE GetAppIDForShortcut() = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE GetAppIDForShortcutObject() = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE GetAppIDForWindow(HWND hWnd, WCHAR** pszAppId, void* pUnknown1, void* pUnknown2, void* pUnknown3) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE GetAppIDForProcess(DWORD dwProcessId, WCHAR** pszAppId, void* pUnknown1, void* pUnknown2, void* pUnknown3) = 0;
|
||||
};
|
||||
|
||||
BOOL GetAppId_7(HWND hWnd, std::wstring* result)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
wil::com_ptr<IAppResolver_7> appResolver;
|
||||
hr = CoCreateInstance(CLSID_StartMenuCacheAndAppResolver, NULL, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, IID_IAppResolver_7, reinterpret_cast<void**>(appResolver.put()));
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
wil::unique_cotaskmem_string pszAppId;
|
||||
hr = appResolver->GetAppIDForWindow(hWnd, &pszAppId, NULL, NULL, NULL);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
*result = std::wstring(pszAppId.get());
|
||||
}
|
||||
|
||||
appResolver->Release();
|
||||
}
|
||||
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
BOOL GetAppId_8(HWND hWnd, std::wstring* result)
|
||||
{
|
||||
HRESULT hr;
|
||||
*result = L"";
|
||||
|
||||
wil::com_ptr<IAppResolver_8> appResolver;
|
||||
hr = CoCreateInstance(CLSID_StartMenuCacheAndAppResolver, NULL, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, IID_IAppResolver_8, reinterpret_cast<void**>(appResolver.put()));
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
wil::unique_cotaskmem_string pszAppId;
|
||||
hr = appResolver->GetAppIDForWindow(hWnd, &pszAppId, NULL, NULL, NULL);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
*result = std::wstring(pszAppId.get());
|
||||
}
|
||||
|
||||
appResolver->Release();
|
||||
}
|
||||
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
BOOL PwaHelper::GetAppId(HWND hWnd, std::wstring* result)
|
||||
{
|
||||
HRESULT hr = GetAppId_8(hWnd, result);
|
||||
if (!SUCCEEDED(hr))
|
||||
{
|
||||
hr = GetAppId_7(hWnd, result);
|
||||
}
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
BOOL GetProcessId_7(DWORD dwProcessId, std::wstring* result)
|
||||
{
|
||||
HRESULT hr;
|
||||
*result = L"";
|
||||
|
||||
wil::com_ptr<IAppResolver_7> appResolver;
|
||||
hr = CoCreateInstance(CLSID_StartMenuCacheAndAppResolver, NULL, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, IID_IAppResolver_7, reinterpret_cast<void**>(appResolver.put()));
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
wil::unique_cotaskmem_string pszAppId;
|
||||
hr = appResolver->GetAppIDForProcess(dwProcessId, &pszAppId, NULL, NULL, NULL);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
*result = std::wstring(pszAppId.get());
|
||||
}
|
||||
|
||||
appResolver->Release();
|
||||
}
|
||||
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
BOOL GetProcessId_8(DWORD dwProcessId, std::wstring* result)
|
||||
{
|
||||
HRESULT hr;
|
||||
*result = L"";
|
||||
|
||||
wil::com_ptr<IAppResolver_8> appResolver;
|
||||
hr = CoCreateInstance(CLSID_StartMenuCacheAndAppResolver, NULL, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, IID_IAppResolver_8, reinterpret_cast<void**>(appResolver.put()));
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
wil::unique_cotaskmem_string pszAppId;
|
||||
hr = appResolver->GetAppIDForProcess(dwProcessId, &pszAppId, NULL, NULL, NULL);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
*result = std::wstring(pszAppId.get());
|
||||
}
|
||||
|
||||
appResolver->Release();
|
||||
}
|
||||
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
BOOL GetProcessId(DWORD dwProcessId, std::wstring* result)
|
||||
{
|
||||
HRESULT hr = GetProcessId_8(dwProcessId, result);
|
||||
if (!SUCCEEDED(hr))
|
||||
{
|
||||
hr = GetProcessId_7(dwProcessId, result);
|
||||
}
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
std::wstring GetProcCommandLine(DWORD pid)
|
||||
{
|
||||
std::wstring commandLine;
|
||||
|
||||
// Open a handle to the process
|
||||
const HANDLE process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
|
||||
if (process == NULL)
|
||||
{
|
||||
Logger::error(L"Failed to open the process, error: {}", get_last_error_or_default(GetLastError()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the address of the ProcessEnvironmentBlock
|
||||
PROCESS_BASIC_INFORMATION pbi = {};
|
||||
NTSTATUS status = NtQueryInformationProcess(process, ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
|
||||
if (status != STATUS_SUCCESS)
|
||||
{
|
||||
Logger::error(L"Failed to query the process, error: {}", status);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the address of the process parameters in the ProcessEnvironmentBlock
|
||||
PEB processEnvironmentBlock = {};
|
||||
if (!ReadProcessMemory(process, pbi.PebBaseAddress, &processEnvironmentBlock, sizeof(processEnvironmentBlock), NULL))
|
||||
{
|
||||
Logger::error(L"Failed to read the process ProcessEnvironmentBlock, error: {}", get_last_error_or_default(GetLastError()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the command line arguments from the process parameters
|
||||
RTL_USER_PROCESS_PARAMETERS params = {};
|
||||
if (!ReadProcessMemory(process, processEnvironmentBlock.ProcessParameters, ¶ms, sizeof(params), NULL))
|
||||
{
|
||||
Logger::error(L"Failed to read the process params, error: {}", get_last_error_or_default(GetLastError()));
|
||||
}
|
||||
else
|
||||
{
|
||||
UNICODE_STRING& commandLineArgs = params.CommandLine;
|
||||
std::vector<WCHAR> buffer(commandLineArgs.Length / sizeof(WCHAR));
|
||||
if (!ReadProcessMemory(process, commandLineArgs.Buffer, buffer.data(), commandLineArgs.Length, NULL))
|
||||
{
|
||||
Logger::error(L"Failed to read the process command line, error: {}", get_last_error_or_default(GetLastError()));
|
||||
}
|
||||
else
|
||||
{
|
||||
commandLine.assign(buffer.data(), buffer.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(process);
|
||||
}
|
||||
|
||||
return commandLine;
|
||||
}
|
||||
|
||||
// Finds all PwaHelper.exe processes with the specified parent process ID
|
||||
std::vector<DWORD> FindPwaHelperProcessIds()
|
||||
{
|
||||
std::vector<DWORD> pwaHelperProcessIds;
|
||||
const HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
|
||||
if (hSnapshot == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
Logger::info(L"Invalid handle when creating snapshot for the search for PwaHelper processes");
|
||||
return pwaHelperProcessIds;
|
||||
}
|
||||
|
||||
PROCESSENTRY32 pe;
|
||||
pe.dwSize = sizeof(PROCESSENTRY32);
|
||||
|
||||
if (Process32First(hSnapshot, &pe))
|
||||
{
|
||||
do
|
||||
{
|
||||
if (_wcsicmp(pe.szExeFile, L"PwaHelper.exe") == 0)
|
||||
{
|
||||
Logger::info(L"Found a PWA process with id {}", pe.th32ProcessID);
|
||||
pwaHelperProcessIds.push_back(pe.th32ProcessID);
|
||||
}
|
||||
} while (Process32Next(hSnapshot, &pe));
|
||||
}
|
||||
|
||||
CloseHandle(hSnapshot);
|
||||
return pwaHelperProcessIds;
|
||||
}
|
||||
|
||||
void PwaHelper::InitAumidToAppId()
|
||||
{
|
||||
if (pwaAumidToAppId.size() > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto pwaHelperProcessIds = FindPwaHelperProcessIds();
|
||||
Logger::info(L"Found {} edge Pwa helper processes", pwaHelperProcessIds.size());
|
||||
for (const auto subProcessID : pwaHelperProcessIds)
|
||||
{
|
||||
std::wstring aumidID;
|
||||
GetProcessId(subProcessID, &aumidID);
|
||||
std::wstring commandLineArg = GetProcCommandLine(subProcessID);
|
||||
auto appIdIndexStart = commandLineArg.find(NonLocalizable::EdgeAppIdIdentifier);
|
||||
if (appIdIndexStart != std::wstring::npos)
|
||||
{
|
||||
commandLineArg = commandLineArg.substr(appIdIndexStart + NonLocalizable::EdgeAppIdIdentifier.size());
|
||||
auto appIdIndexEnd = commandLineArg.find(L" ");
|
||||
if (appIdIndexEnd != std::wstring::npos)
|
||||
{
|
||||
commandLineArg = commandLineArg.substr(0, appIdIndexEnd);
|
||||
}
|
||||
}
|
||||
std::wstring appId{ commandLineArg };
|
||||
pwaAumidToAppId.insert(std::map<std::wstring, std::wstring>::value_type(aumidID, appId));
|
||||
Logger::info(L"Found an edge Pwa helper process with AumidID {} and PwaAppId {}", aumidID, appId);
|
||||
|
||||
PWSTR path = NULL;
|
||||
HRESULT hres = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &path);
|
||||
if (SUCCEEDED(hres))
|
||||
{
|
||||
std::filesystem::path fsPath(path);
|
||||
fsPath /= NonLocalizable::EdgeBase;
|
||||
for (const auto& directory : std::filesystem::directory_iterator(fsPath))
|
||||
{
|
||||
if (directory.is_directory())
|
||||
{
|
||||
const std::filesystem::path directoryName = directory.path().filename();
|
||||
if (directoryName.wstring().find(NonLocalizable::EdgeDirPrefix) == 0)
|
||||
{
|
||||
const std::wstring appIdDir = directoryName.wstring().substr(NonLocalizable::EdgeDirPrefix.size());
|
||||
if (appIdDir == appId)
|
||||
{
|
||||
for (const auto& filename : std::filesystem::directory_iterator(directory))
|
||||
{
|
||||
if (!filename.is_directory())
|
||||
{
|
||||
const std::filesystem::path filenameString = filename.path().filename();
|
||||
if (filenameString.extension().wstring() == L".ico")
|
||||
{
|
||||
pwaAppIdsToAppNames.insert(std::map<std::wstring, std::wstring>::value_type(appId, filenameString.stem().wstring()));
|
||||
Logger::info(L"Storing an edge Pwa app name {} for PwaAppId {}", filenameString.stem().wstring(), appId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CoTaskMemFree(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOL PwaHelper::GetPwaAppId(std::wstring windowAumid, std::wstring* result)
|
||||
{
|
||||
const auto pwaIndex = pwaAumidToAppId.find(windowAumid);
|
||||
if (pwaIndex != pwaAumidToAppId.end())
|
||||
{
|
||||
*result = pwaIndex->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
BOOL PwaHelper::SearchPwaName(std::wstring pwaAppId, std::wstring windowAumid, std::wstring* pwaName)
|
||||
{
|
||||
const auto index = pwaAppIdsToAppNames.find(pwaAppId);
|
||||
if (index != pwaAppIdsToAppNames.end())
|
||||
{
|
||||
*pwaName = index->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::wstring nameFromAumid{ windowAumid };
|
||||
const std::size_t delimiterPos = nameFromAumid.find(L"-");
|
||||
if (delimiterPos != std::string::npos)
|
||||
{
|
||||
nameFromAumid = nameFromAumid.substr(0, delimiterPos);
|
||||
}
|
||||
|
||||
*pwaName = nameFromAumid;
|
||||
return false;
|
||||
}
|
||||
|
||||
void PwaHelper::InitChromeAppIds()
|
||||
{
|
||||
if (chromeAppIds.size() > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PWSTR path = NULL;
|
||||
HRESULT hres = SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &path);
|
||||
if (SUCCEEDED(hres))
|
||||
{
|
||||
std::filesystem::path fsPath(path);
|
||||
fsPath /= NonLocalizable::ChromeBase;
|
||||
for (const auto& directory : std::filesystem::directory_iterator(fsPath))
|
||||
{
|
||||
if (directory.is_directory())
|
||||
{
|
||||
const std::filesystem::path directoryName = directory.path().filename();
|
||||
if (directoryName.wstring().find(NonLocalizable::ChromeDirPrefix) == 0)
|
||||
{
|
||||
const std::wstring appId = directoryName.wstring().substr(NonLocalizable::ChromeDirPrefix.size());
|
||||
chromeAppIds.push_back(appId);
|
||||
for (const auto& filename : std::filesystem::directory_iterator(directory))
|
||||
{
|
||||
if (!filename.is_directory())
|
||||
{
|
||||
const std::filesystem::path filenameString = filename.path().filename();
|
||||
if (filenameString.extension().wstring() == L".ico")
|
||||
{
|
||||
pwaAppIdsToAppNames.insert(std::map<std::wstring, std::wstring>::value_type(appId, filenameString.stem().wstring()));
|
||||
Logger::info(L"Found an installed chrome Pwa app {} with PwaAppId {}", filenameString.stem().wstring(), appId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CoTaskMemFree(path);
|
||||
}
|
||||
}
|
||||
|
||||
BOOL PwaHelper::SearchPwaAppId(std::wstring windowAumid, std::wstring* pwaAppId)
|
||||
{
|
||||
const auto appIdIndexStart = windowAumid.find(NonLocalizable::ChromeAppIdIdentifier);
|
||||
if (appIdIndexStart != std::wstring::npos)
|
||||
{
|
||||
windowAumid = windowAumid.substr(appIdIndexStart + NonLocalizable::ChromeAppIdIdentifier.size());
|
||||
const auto appIdIndexEnd = windowAumid.find(L" ");
|
||||
if (appIdIndexEnd != std::wstring::npos)
|
||||
{
|
||||
windowAumid = windowAumid.substr(0, appIdIndexEnd);
|
||||
}
|
||||
|
||||
const std::wstring windowAumidBegin = windowAumid.substr(0, 10);
|
||||
for (const auto chromeAppId : chromeAppIds)
|
||||
{
|
||||
if (chromeAppId.find(windowAumidBegin) == 0)
|
||||
{
|
||||
*pwaAppId = chromeAppId;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*pwaAppId = L"";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace SnapshotUtils
|
||||
{
|
||||
class PwaHelper
|
||||
{
|
||||
public:
|
||||
void InitAumidToAppId();
|
||||
BOOL GetAppId(HWND hWnd, std::wstring* result);
|
||||
BOOL GetPwaAppId(std::wstring windowAumid, std::wstring* result);
|
||||
BOOL SearchPwaName(std::wstring pwaAppId, std::wstring windowAumid, std::wstring* pwaName);
|
||||
void InitChromeAppIds();
|
||||
BOOL SearchPwaAppId(std::wstring windowAumid, std::wstring* pwaAppId);
|
||||
|
||||
private:
|
||||
std::map<std::wstring, std::wstring> pwaAumidToAppId;
|
||||
std::vector<std::wstring> chromeAppIds;
|
||||
std::map<std::wstring, std::wstring> pwaAppIdsToAppNames;
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
#include "pch.h"
|
||||
#include "SnapshotUtils.h"
|
||||
|
||||
#include <comdef.h>
|
||||
#include <Wbemidl.h>
|
||||
|
||||
#include <common/utils/elevation.h>
|
||||
#include <common/utils/process_path.h>
|
||||
#include <common/notifications/NotificationUtil.h>
|
||||
@@ -9,17 +12,142 @@
|
||||
#include <workspaces-common/WindowFilter.h>
|
||||
|
||||
#include <WorkspacesLib/AppUtils.h>
|
||||
#include <PwaHelper.h>
|
||||
|
||||
#pragma comment(lib, "ntdll.lib")
|
||||
|
||||
namespace SnapshotUtils
|
||||
{
|
||||
namespace NonLocalizable
|
||||
{
|
||||
const std::wstring ApplicationFrameHost = L"ApplicationFrameHost.exe";
|
||||
const std::wstring EdgeFilename = L"msedge.exe";
|
||||
const std::wstring ChromeFilename = L"chrome.exe";
|
||||
}
|
||||
|
||||
class WbemHelper
|
||||
{
|
||||
public:
|
||||
WbemHelper() = default;
|
||||
~WbemHelper()
|
||||
{
|
||||
if (m_services)
|
||||
{
|
||||
m_services->Release();
|
||||
}
|
||||
|
||||
if (m_locator)
|
||||
{
|
||||
m_locator->Release();
|
||||
}
|
||||
}
|
||||
|
||||
bool Initialize()
|
||||
{
|
||||
// Obtain the initial locator to WMI.
|
||||
HRESULT hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, reinterpret_cast<LPVOID*>(&m_locator));
|
||||
if (FAILED(hres))
|
||||
{
|
||||
Logger::error(L"Failed to create IWbemLocator object. Error: {}", get_last_error_or_default(hres));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Connect to WMI through the IWbemLocator::ConnectServer method.
|
||||
hres = m_locator->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &m_services);
|
||||
if (FAILED(hres))
|
||||
{
|
||||
Logger::error(L"Could not connect to WMI. Error: {}", get_last_error_or_default(hres));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set security levels on the proxy.
|
||||
hres = CoSetProxyBlanket(m_services, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
|
||||
if (FAILED(hres))
|
||||
{
|
||||
Logger::error(L"Could not set proxy blanket. Error: {}", get_last_error_or_default(hres));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::wstring GetCommandLineArgs(DWORD processID) const
|
||||
{
|
||||
static std::wstring property = L"CommandLine";
|
||||
std::wstring query = L"SELECT " + property + L" FROM Win32_Process WHERE ProcessId = " + std::to_wstring(processID);
|
||||
return Query(query, property);
|
||||
}
|
||||
|
||||
std::wstring GetExecutablePath(DWORD processID) const
|
||||
{
|
||||
static std::wstring property = L"ExecutablePath";
|
||||
std::wstring query = L"SELECT " + property + L" FROM Win32_Process WHERE ProcessId = " + std::to_wstring(processID);
|
||||
return Query(query, property);
|
||||
}
|
||||
|
||||
private:
|
||||
std::wstring Query(const std::wstring& query, const std::wstring& propertyName) const
|
||||
{
|
||||
if (!m_locator || !m_services)
|
||||
{
|
||||
return L"";
|
||||
}
|
||||
|
||||
IEnumWbemClassObject* pEnumerator = NULL;
|
||||
|
||||
HRESULT hres = m_services->ExecQuery(bstr_t("WQL"), bstr_t(query.c_str()), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);
|
||||
if (FAILED(hres))
|
||||
{
|
||||
Logger::error(L"Query for process failed. Error: {}", get_last_error_or_default(hres));
|
||||
return L"";
|
||||
}
|
||||
|
||||
IWbemClassObject* pClassObject = NULL;
|
||||
ULONG uReturn = 0;
|
||||
std::wstring result = L"";
|
||||
while (pEnumerator)
|
||||
{
|
||||
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pClassObject, &uReturn);
|
||||
if (uReturn == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
VARIANT vtProp;
|
||||
hr = pClassObject->Get(propertyName.c_str(), 0, &vtProp, 0, 0);
|
||||
if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR)
|
||||
{
|
||||
result = vtProp.bstrVal;
|
||||
}
|
||||
VariantClear(&vtProp);
|
||||
|
||||
pClassObject->Release();
|
||||
}
|
||||
|
||||
pEnumerator->Release();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
IWbemLocator* m_locator = NULL;
|
||||
IWbemServices* m_services = NULL;
|
||||
};
|
||||
|
||||
std::wstring GetCommandLineArgs(DWORD processID, const WbemHelper& wbemHelper)
|
||||
{
|
||||
std::wstring executablePath = wbemHelper.GetExecutablePath(processID);
|
||||
std::wstring commandLineArgs = wbemHelper.GetCommandLineArgs(processID);
|
||||
|
||||
if (!commandLineArgs.empty())
|
||||
{
|
||||
auto pos = commandLineArgs.find(executablePath);
|
||||
if (pos != std::wstring::npos)
|
||||
{
|
||||
commandLineArgs = commandLineArgs.substr(pos + executablePath.size());
|
||||
auto spacePos = commandLineArgs.find_first_of(' ');
|
||||
if (spacePos != std::wstring::npos)
|
||||
{
|
||||
commandLineArgs = commandLineArgs.substr(spacePos + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return commandLineArgs;
|
||||
}
|
||||
|
||||
bool IsProcessElevated(DWORD processID)
|
||||
@@ -40,23 +168,16 @@ namespace SnapshotUtils
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsEdge(Utils::Apps::AppData appData)
|
||||
{
|
||||
return appData.installPath.ends_with(NonLocalizable::EdgeFilename);
|
||||
}
|
||||
|
||||
bool IsChrome(Utils::Apps::AppData appData)
|
||||
{
|
||||
return appData.installPath.ends_with(NonLocalizable::ChromeFilename);
|
||||
}
|
||||
|
||||
std::vector<WorkspacesData::WorkspacesProject::Application> GetApps(const std::function<unsigned int(HWND)> getMonitorNumberFromWindowHandle, const std::function<WorkspacesData::WorkspacesProject::Monitor::MonitorRect(unsigned int)> getMonitorRect)
|
||||
{
|
||||
PwaHelper pwaHelper{};
|
||||
std::vector<WorkspacesData::WorkspacesProject::Application> apps{};
|
||||
|
||||
auto installedApps = Utils::Apps::GetAppsList();
|
||||
auto windows = WindowEnumerator::Enumerate(WindowFilter::Filter);
|
||||
|
||||
// for command line args detection
|
||||
// WbemHelper wbemHelper;
|
||||
// wbemHelper.Initialize();
|
||||
|
||||
for (const auto window : windows)
|
||||
{
|
||||
@@ -111,7 +232,7 @@ namespace SnapshotUtils
|
||||
if (pid != otherPid && title == WindowUtils::GetWindowTitle(otherWindow))
|
||||
{
|
||||
processPath = get_process_path(otherPid);
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,48 +249,6 @@ namespace SnapshotUtils
|
||||
continue;
|
||||
}
|
||||
|
||||
std::wstring pwaAppId = L"";
|
||||
std::wstring finalName = data.value().name;
|
||||
std::wstring pwaName = L"";
|
||||
if (IsEdge(data.value()))
|
||||
{
|
||||
pwaHelper.InitAumidToAppId();
|
||||
|
||||
std::wstring windowAumid;
|
||||
pwaHelper.GetAppId(window, &windowAumid);
|
||||
Logger::info(L"Found an edge window with aumid {}", windowAumid);
|
||||
|
||||
if (pwaHelper.GetPwaAppId(windowAumid, &pwaAppId))
|
||||
{
|
||||
Logger::info(L"The found edge window is a PWA app with appId {}", pwaAppId);
|
||||
if (pwaHelper.SearchPwaName(pwaAppId, windowAumid ,& pwaName))
|
||||
{
|
||||
Logger::info(L"The found edge window is a PWA app with name {}", finalName);
|
||||
}
|
||||
finalName = pwaName + L" (" + finalName + L")";
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::info(L"The found edge window does not contain a PWA app", pwaAppId);
|
||||
}
|
||||
}
|
||||
else if (IsChrome(data.value()))
|
||||
{
|
||||
pwaHelper.InitChromeAppIds();
|
||||
|
||||
std::wstring windowAumid;
|
||||
pwaHelper.GetAppId(window, &windowAumid);
|
||||
Logger::info(L"Found a chrome window with aumid {}", windowAumid);
|
||||
|
||||
if (pwaHelper.SearchPwaAppId(windowAumid, &pwaAppId))
|
||||
{
|
||||
if (pwaHelper.SearchPwaName(pwaAppId, windowAumid, &pwaName))
|
||||
{
|
||||
finalName = pwaName + L" (" + finalName + L")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool isMinimized = WindowUtils::IsMinimized(window);
|
||||
unsigned int monitorNumber = getMonitorNumberFromWindowHandle(window);
|
||||
|
||||
@@ -184,13 +263,12 @@ namespace SnapshotUtils
|
||||
}
|
||||
|
||||
WorkspacesData::WorkspacesProject::Application app{
|
||||
.name = finalName,
|
||||
.name = data.value().name,
|
||||
.title = title,
|
||||
.path = data.value().installPath,
|
||||
.packageFullName = data.value().packageFullName,
|
||||
.appUserModelId = data.value().appUserModelId,
|
||||
.pwaAppId = pwaAppId,
|
||||
.commandLineArgs = L"",
|
||||
.commandLineArgs = L"", // GetCommandLineArgs(pid, wbemHelper),
|
||||
.isElevated = IsProcessElevated(pid),
|
||||
.canLaunchElevated = data.value().canLaunchElevated,
|
||||
.isMinimized = isMinimized,
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>shcore.lib;Shell32.lib;propsys.lib;DbgHelp.lib</AdditionalDependencies>
|
||||
<AdditionalDependencies>shcore.lib;Shell32.lib;propsys.lib;DbgHelp.lib;wbemuuid.lib</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
||||
@@ -130,12 +130,10 @@
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PwaHelper.cpp" />
|
||||
<ClCompile Include="SnapshotUtils.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="PwaHelper.h" />
|
||||
<ClInclude Include="resource.base.h" />
|
||||
<ClInclude Include="SnapshotUtils.h" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -24,9 +24,6 @@
|
||||
<ClInclude Include="resource.base.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PwaHelper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
@@ -38,9 +35,6 @@
|
||||
<ClCompile Include="SnapshotUtils.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PwaHelper.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
||||
@@ -68,7 +68,6 @@ namespace Microsoft.Plugin.WindowWalker.Components
|
||||
searchResult.Result.SwitchToWindow();
|
||||
return true;
|
||||
},
|
||||
Score = searchResult.Score,
|
||||
|
||||
// For debugging you can set the second parameter to true to see more information.
|
||||
ToolTipData = GetToolTip(searchResult.Result, false),
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -23,7 +22,6 @@ using Peek.FilePreviewer.Models;
|
||||
using Peek.FilePreviewer.Previewers.Helpers;
|
||||
using Peek.FilePreviewer.Previewers.Interfaces;
|
||||
using Windows.Foundation;
|
||||
using Windows.Graphics.Imaging;
|
||||
|
||||
namespace Peek.FilePreviewer.Previewers
|
||||
{
|
||||
@@ -60,12 +58,6 @@ namespace Peek.FilePreviewer.Previewers
|
||||
|
||||
private DispatcherQueue Dispatcher { get; }
|
||||
|
||||
private static readonly HashSet<string> _supportedFileTypes =
|
||||
BitmapDecoder.GetDecoderInformationEnumerator()
|
||||
.SelectMany(di => di.FileExtensions)
|
||||
.Union([".svg", ".qoi"])
|
||||
.ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public static bool IsItemSupported(IFileSystemItem item)
|
||||
{
|
||||
return _supportedFileTypes.Contains(item.Extension);
|
||||
@@ -207,5 +199,74 @@ namespace Peek.FilePreviewer.Previewers
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private static readonly HashSet<string> _supportedFileTypes = new HashSet<string>
|
||||
{
|
||||
// Image types
|
||||
".bmp",
|
||||
".gif",
|
||||
".jpg",
|
||||
".jfif",
|
||||
".jfi",
|
||||
".jif",
|
||||
".jpeg",
|
||||
".jpe",
|
||||
".png",
|
||||
".tif", // very slow for large files: no thumbnail?
|
||||
".tiff", // NEED TO TEST
|
||||
".dib", // NEED TO TEST
|
||||
".heic",
|
||||
".heif",
|
||||
".hif", // NEED TO TEST
|
||||
".avif", // NEED TO TEST
|
||||
".jxr",
|
||||
".wdp",
|
||||
".ico", // NEED TO TEST
|
||||
".thumb", // NEED TO TEST
|
||||
".webp",
|
||||
|
||||
// Raw types
|
||||
".arw",
|
||||
".cr2",
|
||||
".crw",
|
||||
".erf",
|
||||
".kdc", // NEED TO TEST
|
||||
".mrw",
|
||||
".nef",
|
||||
".nrw",
|
||||
".orf",
|
||||
".pef",
|
||||
".raf",
|
||||
".raw",
|
||||
".rw2",
|
||||
".rwl",
|
||||
".sr2",
|
||||
".srw",
|
||||
".srf",
|
||||
".dcs", // NEED TO TEST
|
||||
".dcr",
|
||||
".drf", // NEED TO TEST
|
||||
".k25",
|
||||
".3fr",
|
||||
".ari", // NEED TO TEST
|
||||
".bay", // NEED TO TEST
|
||||
".cap", // NEED TO TEST
|
||||
".iiq",
|
||||
".eip", // NEED TO TEST
|
||||
".fff",
|
||||
".mef",
|
||||
|
||||
// ".mdc", // Crashes in GetFullBitmapFromPathAsync
|
||||
".mos",
|
||||
".R3D",
|
||||
".rwz", // NEED TO TEST
|
||||
".x3f",
|
||||
".ori",
|
||||
".cr3",
|
||||
|
||||
".svg",
|
||||
|
||||
".qoi",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,11 +20,10 @@ namespace Peek.FilePreviewer.Previewers
|
||||
|
||||
public static HashSet<string> GetExtensions()
|
||||
{
|
||||
HashSet<string> set = [];
|
||||
|
||||
HashSet<string> set = new HashSet<string>();
|
||||
try
|
||||
{
|
||||
using JsonDocument languageListDocument = Microsoft.PowerToys.FilePreviewCommon.MonacoHelper.GetLanguages();
|
||||
JsonDocument languageListDocument = Microsoft.PowerToys.FilePreviewCommon.MonacoHelper.GetLanguages();
|
||||
JsonElement languageList = languageListDocument.RootElement.GetProperty("list");
|
||||
foreach (JsonElement e in languageList.EnumerateArray())
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.props" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
|
||||
<PropertyGroup Label="Globals">
|
||||
@@ -209,7 +209,7 @@
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2739.15\build\native\Microsoft.Web.WebView2.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2739.15\build\native\Microsoft.Web.WebView2.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||
<Import Project="..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.targets" Condition="Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
@@ -223,8 +223,8 @@
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.SDK.BuildTools.10.0.22621.2428\build\Microsoft.Windows.SDK.BuildTools.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2739.15\build\native\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Web.WebView2.1.0.2739.15\build\native\Microsoft.Web.WebView2.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.241114003\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.props'))" />
|
||||
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.WindowsAppSDK.1.6.240923002\build\native\Microsoft.WindowsAppSDK.targets'))" />
|
||||
</Target>
|
||||
<Target Name="AddWildCardItems" AfterTargets="BuildGenerateSources">
|
||||
<ItemGroup>
|
||||
|
||||
@@ -65,7 +65,4 @@
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="PowerRenameUI.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -6,5 +6,5 @@
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.SDK.BuildTools" version="10.0.22621.2428" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK" version="1.6.241114003" targetFramework="native" />
|
||||
<package id="Microsoft.WindowsAppSDK" version="1.6.240923002" targetFramework="native" />
|
||||
</packages>
|
||||
@@ -14,113 +14,10 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
public HotkeySettings DefaultActivationShortcut => new HotkeySettings(true, false, false, true, 0x44);
|
||||
|
||||
[JsonPropertyName("activation_shortcut")]
|
||||
public HotkeySettings ActivationShortcut
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public HotkeySettings ActivationShortcut { get; set; }
|
||||
|
||||
[JsonPropertyName("thumbnail_size")]
|
||||
public MouseJumpThumbnailSize ThumbnailSize
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the preview type.
|
||||
/// Allowed values are "compact", "bezelled", "custom"
|
||||
/// </summary>
|
||||
[JsonPropertyName("preview_type")]
|
||||
public string PreviewType
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("background_color_1")]
|
||||
public string BackgroundColor1
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("background_color_2")]
|
||||
public string BackgroundColor2
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("border_thickness")]
|
||||
public int BorderThickness
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("border_color")]
|
||||
public string BorderColor
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("border_3d_depth")]
|
||||
public int Border3dDepth
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("border_padding")]
|
||||
public int BorderPadding
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("bezel_thickness")]
|
||||
public int BezelThickness
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("bezel_color")]
|
||||
public string BezelColor
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("bezel_3d_depth")]
|
||||
public int Bezel3dDepth
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("screen_margin")]
|
||||
public int ScreenMargin
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("screen_color_1")]
|
||||
public string ScreenColor1
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[JsonPropertyName("screen_color_2")]
|
||||
public string ScreenColor2
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
public MouseJumpThumbnailSize ThumbnailSize { get; set; }
|
||||
|
||||
public MouseJumpProperties()
|
||||
{
|
||||
|
||||
@@ -7,8 +7,6 @@ using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
using MouseJump.Common.Helpers;
|
||||
using MouseJump.Common.Models.Settings;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
@@ -28,7 +26,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
{
|
||||
Name = ModuleName;
|
||||
Properties = new MouseJumpProperties();
|
||||
Version = "1.1";
|
||||
Version = "1.0";
|
||||
}
|
||||
|
||||
public void Save(ISettingsUtils settingsUtils)
|
||||
@@ -49,74 +47,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
// This can be utilized in the future if the settings.json file is to be modified/deleted.
|
||||
public bool UpgradeSettingsConfiguration()
|
||||
{
|
||||
/*
|
||||
v1.0 - initial version
|
||||
|
||||
* DefaultActivationShortcut
|
||||
* activation_shortcut
|
||||
* thumbnail_size
|
||||
* name
|
||||
* version
|
||||
*/
|
||||
var upgraded = false;
|
||||
|
||||
if (this.Version == "1.0")
|
||||
{
|
||||
/*
|
||||
v1.1 - added preview style settings
|
||||
|
||||
* preview_type
|
||||
* background_color_1
|
||||
* background_color_2
|
||||
* border_thickness
|
||||
* border_color
|
||||
* border_3d_depth
|
||||
* border_padding
|
||||
* bezel_thickness
|
||||
* bezel_color
|
||||
* bezel_3d_depth
|
||||
* screen_margin
|
||||
* screen_color_1
|
||||
* screen_color_2
|
||||
*/
|
||||
this.Version = "1.1";
|
||||
|
||||
// note - there's an issue where ITwoWayPipeMessageIPCManagedMethods.Send overwrites
|
||||
// the settings file version as "1.0" regardless of the actual version. as a result,
|
||||
// the UpgradeSettingsConfiguration can get triggered even if the config has already
|
||||
// been upgraded, so we need to do an additional check to make sure values haven't
|
||||
// already been upgraded before we overwrite them with default values.
|
||||
if (string.IsNullOrEmpty(this.Properties.PreviewType))
|
||||
{
|
||||
// set default values for custom preview style
|
||||
var previewStyle = StyleHelper.BezelledPreviewStyle;
|
||||
this.Properties.PreviewType = PreviewType.Bezelled.ToString();
|
||||
this.Properties.BackgroundColor1 = ConfigHelper.SerializeToConfigColorString(
|
||||
ConfigHelper.ToUnnamedColor(previewStyle.CanvasStyle.BackgroundStyle.Color1));
|
||||
this.Properties.BackgroundColor2 = ConfigHelper.SerializeToConfigColorString(
|
||||
ConfigHelper.ToUnnamedColor(previewStyle.CanvasStyle.BackgroundStyle.Color2));
|
||||
this.Properties.BorderThickness = (int)previewStyle.CanvasStyle.BorderStyle.Top;
|
||||
this.Properties.BorderColor = ConfigHelper.SerializeToConfigColorString(
|
||||
ConfigHelper.ToUnnamedColor(previewStyle.CanvasStyle.BorderStyle.Color));
|
||||
this.Properties.Border3dDepth = (int)previewStyle.CanvasStyle.BorderStyle.Depth;
|
||||
this.Properties.BorderPadding = (int)previewStyle.CanvasStyle.PaddingStyle.Top;
|
||||
this.Properties.BezelThickness = (int)previewStyle.ScreenStyle.BorderStyle.Top;
|
||||
this.Properties.BezelColor = ConfigHelper.SerializeToConfigColorString(
|
||||
ConfigHelper.ToUnnamedColor(previewStyle.ScreenStyle.BorderStyle.Color));
|
||||
this.Properties.Bezel3dDepth = (int)previewStyle.ScreenStyle.BorderStyle.Depth;
|
||||
this.Properties.ScreenMargin = (int)previewStyle.ScreenStyle.MarginStyle.Top;
|
||||
this.Properties.ScreenColor1 = ConfigHelper.SerializeToConfigColorString(
|
||||
ConfigHelper.ToUnnamedColor(previewStyle.ScreenStyle.BackgroundStyle.Color1));
|
||||
this.Properties.ScreenColor2 = ConfigHelper.SerializeToConfigColorString(
|
||||
ConfigHelper.ToUnnamedColor(previewStyle.ScreenStyle.BackgroundStyle.Color2));
|
||||
}
|
||||
|
||||
// we still need to flag the settings as "upgraded" so that the new version gets written
|
||||
// back to the config file, even if we didn't actually change and setting values
|
||||
upgraded = true;
|
||||
}
|
||||
|
||||
return upgraded;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user