mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-01-17 09:38:13 +01:00
Compare commits
18 Commits
dev/addNew
...
dev/leilzh
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f2e454c48 | ||
|
|
13a49ad5a8 | ||
|
|
05e54e64c8 | ||
|
|
eadcf4b120 | ||
|
|
e7f17495bb | ||
|
|
89be43e15d | ||
|
|
e0949cb4ea | ||
|
|
35b9bcacdb | ||
|
|
50b1342234 | ||
|
|
866518e119 | ||
|
|
fe610d4d0e | ||
|
|
54aab5d109 | ||
|
|
db23992aac | ||
|
|
0ee1befe7b | ||
|
|
f565874fc8 | ||
|
|
438ee39252 | ||
|
|
28304838af | ||
|
|
fc29fc7426 |
5
.github/actions/spell-check/allow/code.txt
vendored
5
.github/actions/spell-check/allow/code.txt
vendored
@@ -226,3 +226,8 @@ arcosh
|
||||
dbus
|
||||
anypass
|
||||
gpg
|
||||
|
||||
# .NET
|
||||
|
||||
AOT
|
||||
Aot
|
||||
|
||||
10
.github/actions/spell-check/allow/names.txt
vendored
10
.github/actions/spell-check/allow/names.txt
vendored
@@ -44,6 +44,7 @@ Bartosz
|
||||
betadele
|
||||
betsegaw
|
||||
bricelam
|
||||
bsky
|
||||
CCcat
|
||||
Chinh
|
||||
chrdavis
|
||||
@@ -53,6 +54,7 @@ Coplen
|
||||
craigloewen
|
||||
crutkas
|
||||
damienleroy
|
||||
daverayment
|
||||
davidegiacometti
|
||||
debian
|
||||
Deibisu
|
||||
@@ -64,6 +66,7 @@ Essey
|
||||
ethanfangg
|
||||
ferraridavide
|
||||
frankychen
|
||||
Gaarden
|
||||
gaardmark
|
||||
gabime
|
||||
Galaxi
|
||||
@@ -71,6 +74,7 @@ Garside
|
||||
Gershaft
|
||||
Giordani
|
||||
Gokce
|
||||
gordon
|
||||
grzhan
|
||||
Guo
|
||||
hanselman
|
||||
@@ -82,6 +86,7 @@ Horvalds
|
||||
Howett
|
||||
htcfreek
|
||||
Huynh
|
||||
Ionut
|
||||
Jaswal
|
||||
jefflord
|
||||
Jordi
|
||||
@@ -90,11 +95,13 @@ Kairu
|
||||
Kamra
|
||||
Kantarci
|
||||
Karthick
|
||||
kaylacinnamon
|
||||
kevinguo
|
||||
Krigun
|
||||
Lambson
|
||||
Laute
|
||||
laviusmotileng
|
||||
Leilei
|
||||
Luecking
|
||||
Mahalingam
|
||||
Markovic
|
||||
@@ -110,6 +117,7 @@ nathancartlidge
|
||||
Nemeth
|
||||
nielslaute
|
||||
oldnewthing
|
||||
onegreatworld
|
||||
palenshus
|
||||
pedrolamas
|
||||
peteblois
|
||||
@@ -120,6 +128,7 @@ Pylyp
|
||||
quachpas
|
||||
Quriz
|
||||
randyrants
|
||||
rayment
|
||||
ricardosantos
|
||||
riri
|
||||
ritchielawrence
|
||||
@@ -148,6 +157,7 @@ waaverecords
|
||||
Whuihuan
|
||||
Xpg
|
||||
ycv
|
||||
yeelam
|
||||
Yuniardi
|
||||
yuyoyuppe
|
||||
Zeol
|
||||
|
||||
2
.github/actions/spell-check/expect.txt
vendored
2
.github/actions/spell-check/expect.txt
vendored
@@ -667,6 +667,7 @@ Iindex
|
||||
IJson
|
||||
Ijwhost
|
||||
IKs
|
||||
iljxck
|
||||
ILogon
|
||||
IMAGEHLP
|
||||
IMAGERESIZERCONTEXTMENU
|
||||
@@ -770,6 +771,7 @@ lastcodeanalysissucceeded
|
||||
Lastdevice
|
||||
LASTEXITCODE
|
||||
LAYOUTRTL
|
||||
lcb
|
||||
LCIDTo
|
||||
lcl
|
||||
Lclean
|
||||
|
||||
@@ -28,7 +28,6 @@ parameters:
|
||||
type: object
|
||||
default:
|
||||
- x64
|
||||
- arm64
|
||||
- name: enableMsBuildCaching
|
||||
type: boolean
|
||||
displayName: "Enable MSBuild Caching"
|
||||
|
||||
39
.pipelines/v2/templates/job-fuzz.yml
Normal file
39
.pipelines/v2/templates/job-fuzz.yml
Normal file
@@ -0,0 +1,39 @@
|
||||
parameters:
|
||||
- name: configuration
|
||||
type: string
|
||||
default: "Release"
|
||||
- name: platform
|
||||
type: string
|
||||
default: ""
|
||||
- name: inputArtifactStem
|
||||
type: string
|
||||
default: ""
|
||||
|
||||
jobs:
|
||||
- job: OneFuzz
|
||||
pool:
|
||||
vmImage: windows-2022
|
||||
|
||||
steps:
|
||||
- checkout: self
|
||||
submodules: false
|
||||
clean: true
|
||||
fetchDepth: 1
|
||||
fetchTags: false
|
||||
|
||||
- download: current
|
||||
displayName: Download artifacts
|
||||
artifact: build-${{ parameters.platform }}-${{ parameters.configuration }}${{ parameters.inputArtifactStem }}
|
||||
patterns: |-
|
||||
**/tests/*.FuzzTests/**
|
||||
- script: |
|
||||
echo "Printing files in target directory..."
|
||||
dir "$(Pipeline.Workspace)\build-${{ parameters.platform }}-${{ parameters.configuration }}${{ parameters.inputArtifactStem }}\x64\Release\tests\AdvancedPaste.FuzzTests\net8.0-windows10.0.19041.0"
|
||||
displayName: "Print files in target directory"
|
||||
|
||||
- task: onefuzz-task@0
|
||||
inputs:
|
||||
onefuzzOSes: Windows
|
||||
env:
|
||||
onefuzzDropDirectory: $(Pipeline.Workspace)\build-${{ parameters.platform }}-${{ parameters.configuration }}${{ parameters.inputArtifactStem }}\x64\Release\tests\AdvancedPaste.FuzzTests\net8.0-windows10.0.19041.0
|
||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||
@@ -4,8 +4,8 @@ variables:
|
||||
- name: EnablePipelineCache
|
||||
value: true
|
||||
- ${{ if eq(parameters.enableMsBuildCaching, true) }}:
|
||||
- name: EnablePipelineCache
|
||||
value: true
|
||||
- name: EnablePipelineCache
|
||||
value: true
|
||||
|
||||
parameters:
|
||||
- name: buildPlatforms
|
||||
@@ -26,43 +26,54 @@ parameters:
|
||||
stages:
|
||||
# Allow manual builds to skip pre-check
|
||||
- ${{ if ne(variables['Build.Reason'], 'Manual') }}:
|
||||
- stage: Precheck
|
||||
jobs:
|
||||
- template: job-ci-precheck.yml
|
||||
- stage: Precheck
|
||||
jobs:
|
||||
- template: job-ci-precheck.yml
|
||||
|
||||
- ${{ each platform in parameters.buildPlatforms }}:
|
||||
- stage: Build_${{ platform }}
|
||||
displayName: Build ${{ platform }}
|
||||
${{ if ne(variables['Build.Reason'], 'Manual') }}:
|
||||
dependsOn: [Precheck]
|
||||
condition: and(succeeded(), ne(dependencies.Precheck.outputs['Precheck.verifyBuildRequest.skipBuild'], 'Yes'))
|
||||
${{ else }}:
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: job-build-project.yml
|
||||
parameters:
|
||||
pool:
|
||||
${{ if eq(variables['System.CollectionId'], 'cb55739e-4afe-46a3-970f-1b49d8ee7564') }}:
|
||||
name: SHINE-INT-L
|
||||
${{ else }}:
|
||||
name: SHINE-OSS-L
|
||||
${{ if eq(parameters.useVSPreview, true) }}:
|
||||
demands: ImageOverride -equals SHINE-VS17-Preview
|
||||
buildPlatforms:
|
||||
- ${{ platform }}
|
||||
buildConfigurations: [Release]
|
||||
enablePackageCaching: true
|
||||
enableMsBuildCaching: ${{ parameters.enableMsBuildCaching }}
|
||||
runTests: ${{ parameters.runTests }}
|
||||
useVSPreview: ${{ parameters.useVSPreview }}
|
||||
|
||||
- ${{ if eq(parameters.runTests, true) }}:
|
||||
- stage: Test_${{ platform }}
|
||||
displayName: Test ${{ platform }}
|
||||
dependsOn:
|
||||
- Build_${{platform}}
|
||||
- stage: Build_${{ platform }}
|
||||
displayName: Build ${{ platform }}
|
||||
${{ if ne(variables['Build.Reason'], 'Manual') }}:
|
||||
dependsOn: [Precheck]
|
||||
condition: and(succeeded(), ne(dependencies.Precheck.outputs['Precheck.verifyBuildRequest.skipBuild'], 'Yes'))
|
||||
${{ else }}:
|
||||
dependsOn: []
|
||||
jobs:
|
||||
- template: job-test-project.yml
|
||||
- template: job-build-project.yml
|
||||
parameters:
|
||||
pool:
|
||||
${{ if eq(variables['System.CollectionId'], 'cb55739e-4afe-46a3-970f-1b49d8ee7564') }}:
|
||||
name: SHINE-INT-L
|
||||
${{ else }}:
|
||||
name: SHINE-OSS-L
|
||||
${{ if eq(parameters.useVSPreview, true) }}:
|
||||
demands: ImageOverride -equals SHINE-VS17-Preview
|
||||
buildPlatforms:
|
||||
- ${{ platform }}
|
||||
buildConfigurations: [Release]
|
||||
enablePackageCaching: true
|
||||
enableMsBuildCaching: ${{ parameters.enableMsBuildCaching }}
|
||||
runTests: ${{ parameters.runTests }}
|
||||
useVSPreview: ${{ parameters.useVSPreview }}
|
||||
|
||||
# - ${{ if eq(parameters.runTests, true) }}:
|
||||
# - stage: Test_${{ platform }}
|
||||
# displayName: Test ${{ platform }}
|
||||
# dependsOn:
|
||||
# - Build_${{platform}}
|
||||
# jobs:
|
||||
# - template: job-test-project.yml
|
||||
# parameters:
|
||||
# platform: ${{ platform }}
|
||||
# configuration: Release
|
||||
|
||||
- stage: Fuzz ${platform}
|
||||
displayName: Fuzz ${{ platform }}
|
||||
dependsOn:
|
||||
- Build_${platform}
|
||||
jobs:
|
||||
- template: job-fuzz.yml
|
||||
parameters:
|
||||
platform: ${{ platform }}
|
||||
configuration: Release
|
||||
inputArtifactStem: ""
|
||||
|
||||
21
COMMUNITY.md
21
COMMUNITY.md
@@ -9,12 +9,18 @@ Names are in alphabetical order based on first name.
|
||||
### [@Aaron-Junker](https://github.com/Aaron-Junker) - [Aaron Junker](https://aaron-junker.github.io)
|
||||
Aaron has helped triaging, discussing, and creating a substantial number of issues and contributed features/fixes. Aaron was the primary person for helping build the File Explorer preview pane handler for developer files.
|
||||
|
||||
### [@cgaarden](https://github.com/cgaarden) - [Christian Gaarden Gaardmark](https://www.onegreatworld.com)
|
||||
Christian contributed New+ utility
|
||||
|
||||
### [@CleanCodeDeveloper](https://github.com/CleanCodeDeveloper)
|
||||
CleanCodeDeveloper helped do massive amounts of code stability and image resizer work.
|
||||
|
||||
### [@damienleroy](https://github.com/damienleroy) - [Damien Leroy](https://www.linkedin.com/in/Damien-Leroy-b2734416a/)
|
||||
Damien has helped out by developing and contributing the Quick Accent utility.
|
||||
|
||||
### [@daverayment ](https://github.com/daverayment ) - [David Rayment](https://www.linkedin.com/in/david-rayment-168b5251/)
|
||||
Dave has helped improve the experience inside of Peek by adding in new features and fixing bugs.
|
||||
|
||||
### [@davidegiacometti](https://github.com/davidegiacometti) - [Davide Giacometti](https://www.linkedin.com/in/davidegiacometti/)
|
||||
Davide has helped fix multiple bugs, added new utilities, features, as well as help us with the ARM64 effort by porting applications to .NET Core.
|
||||
|
||||
@@ -42,6 +48,9 @@ Color Picker is from Martin.
|
||||
### [@mikeclayton](https://github.com/mikeclayton) - [Michael Clayton](https://michael-clayton.com)
|
||||
Michael contributed the [initial version](https://github.com/microsoft/PowerToys/issues/23216) of the Mouse Jump tool and [a number of updates](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+author%3Amikeclayton) based on his FancyMouse utility.
|
||||
|
||||
### [@PesBandi](https://github.com/PesBandi/) - PesBandi
|
||||
PesBandi has helped do massive amounts of Quick Accent and bug fixes.
|
||||
|
||||
### [@riverar](https://github.com/riverar) - [Rafael Rivera](https://withinrafael.com/)
|
||||
Rafael has helped do the [upgrade from CppWinRT 1.x to 2.0](https://github.com/microsoft/PowerToys/issues/1907). He directly provided feedback to the CppWinRT team for bugs from this migration as well.
|
||||
|
||||
@@ -154,18 +163,23 @@ Other contributors:
|
||||
## PowerToys core team
|
||||
|
||||
- [@crutkas](https://github.com/crutkas/) - Clint Rutkas - Lead
|
||||
- [@ethanfangg](https://github.com/ethanfangg) - Ethan Fang - Lead
|
||||
- [@cinnamon-msft](https://github.com/cinnamon-msft) - Kayla Cinnamon - Product Manager
|
||||
- [@cinnamon-msft](https://github.com/cinnamon-msft) - Kayla Cinnamon - Lead
|
||||
- [@ethanfangg](https://github.com/ethanfangg) - Ethan Fang - Product Manager
|
||||
- [@plante-msft](https://github.com/plante-msft) - Connor Plante - Product Manager
|
||||
- [@nguyen-dows](https://github.com/nguyen-dows) - Christopher Nguyen - Product Manager
|
||||
- [@joadoumie](https://github.com/joadoumie) - Jordi Adoumie - Product Manager
|
||||
- [@jaimecbernardo](https://github.com/jaimecbernardo) - Jaime Bernardo - Dev lead
|
||||
- [@dhowett](https://github.com/dhowett) - Dustin Howett - Dev lead
|
||||
- [@yeelam-gordon](https://github.com/yeelam-gordon) - Gordon Lam - Dev lead
|
||||
- [@drawbyperpetual](https://github.com/drawbyperpetual) - Anirudha Shankar - Dev
|
||||
- [@mantaionut](https://github.com/mantaionut) - Ionut Manta - Dev
|
||||
- [@donlaci](https://github.com/donlaci) - Laszlo Nemeth - Dev
|
||||
- [@gokcekantarci](https://github.com/gokcekantarci) - Gokce Kantarci - Dev
|
||||
- [@SeraphimaZykova](https://github.com/SeraphimaZykova) - Seraphima Zykova - Dev
|
||||
- [@stefansjfw](https://github.com/stefansjfw) - Stefan Markovic - Dev
|
||||
- [@lei9444](https://github.com/lei9444) - Leilei Zhang - Dev
|
||||
- [@shuaiyuanxx](https://github.com/shuaiyuanxx) - Shawn Yuan - Dev
|
||||
- [@moooyo](https://github.com/moooyo) - Yu Leng - Dev
|
||||
- [@haoliuu](https://github.com/haoliuu) - Hao Liu - Dev
|
||||
|
||||
# Former PowerToys core team members
|
||||
|
||||
@@ -176,3 +190,4 @@ Other contributors:
|
||||
- [@mykhailopylyp](https://github.com/mykhailopylyp) - Mykhailo Pylyp - Dev
|
||||
- [@taras-janea](https://github.com/taras-janea) - Taras Sich - Dev
|
||||
- [@yuyoyuppe](https://github.com/yuyoyuppe) - Andrey Nekrasov - Dev
|
||||
- [@gokcekantarci](https://github.com/gokcekantarci) - Gokce Kantarci - Dev
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.0" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
|
||||
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
|
||||
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.2739.15" />
|
||||
<!-- Package Microsoft.Win32.SystemEvents added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Drawing.Common but the 8.0.1 version wasn't published to nuget. -->
|
||||
|
||||
@@ -171,6 +171,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
src\.editorconfig = src\.editorconfig
|
||||
.vsconfig = .vsconfig
|
||||
src\Common.Dotnet.AotCompatibility.props = src\Common.Dotnet.AotCompatibility.props
|
||||
src\Common.Dotnet.CsWinRT.props = src\Common.Dotnet.CsWinRT.props
|
||||
src\Common.SelfContained.props = src\Common.SelfContained.props
|
||||
Cpp.Build.props = Cpp.Build.props
|
||||
@@ -634,6 +635,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkspacesCsharpLibrary", "
|
||||
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
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdvancedPaste.FuzzTests", "src\modules\AdvancedPaste\AdvancedPaste.FuzzTests\AdvancedPaste.FuzzTests.csproj", "{4D079CB1-E2FE-417D-9DB4-9D0B03BC80C6}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|ARM64 = Debug|ARM64
|
||||
@@ -2806,6 +2809,18 @@ Global
|
||||
{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
|
||||
{4D079CB1-E2FE-417D-9DB4-9D0B03BC80C6}.Debug|ARM64.ActiveCfg = Debug|ARM64
|
||||
{4D079CB1-E2FE-417D-9DB4-9D0B03BC80C6}.Debug|ARM64.Build.0 = Debug|ARM64
|
||||
{4D079CB1-E2FE-417D-9DB4-9D0B03BC80C6}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{4D079CB1-E2FE-417D-9DB4-9D0B03BC80C6}.Debug|x64.Build.0 = Debug|x64
|
||||
{4D079CB1-E2FE-417D-9DB4-9D0B03BC80C6}.Debug|x86.ActiveCfg = Debug|x64
|
||||
{4D079CB1-E2FE-417D-9DB4-9D0B03BC80C6}.Debug|x86.Build.0 = Debug|x64
|
||||
{4D079CB1-E2FE-417D-9DB4-9D0B03BC80C6}.Release|ARM64.ActiveCfg = Release|ARM64
|
||||
{4D079CB1-E2FE-417D-9DB4-9D0B03BC80C6}.Release|ARM64.Build.0 = Release|ARM64
|
||||
{4D079CB1-E2FE-417D-9DB4-9D0B03BC80C6}.Release|x64.ActiveCfg = Release|x64
|
||||
{4D079CB1-E2FE-417D-9DB4-9D0B03BC80C6}.Release|x64.Build.0 = Release|x64
|
||||
{4D079CB1-E2FE-417D-9DB4-9D0B03BC80C6}.Release|x86.ActiveCfg = Release|x64
|
||||
{4D079CB1-E2FE-417D-9DB4-9D0B03BC80C6}.Release|x86.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -3039,6 +3054,7 @@ Global
|
||||
{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}
|
||||
{4D079CB1-E2FE-417D-9DB4-9D0B03BC80C6} = {9873BA05-4C41-4819-9283-CF45D795431B}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Microsoft PowerToys
|
||||
|
||||

|
||||

|
||||
|
||||
[How to use PowerToys][usingPowerToys-docs-link] | [Downloads & Release notes][github-release-link] | [Contributing to PowerToys](#contributing) | [What's Happening](#whats-happening) | [Roadmap](#powertoys-roadmap)
|
||||
|
||||
@@ -20,6 +20,12 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
|
||||
| [Registry Preview](https://aka.ms/PowerToysOverview_RegistryPreview) | [Screen Ruler](https://aka.ms/PowerToysOverview_ScreenRuler) | [Shortcut Guide](https://aka.ms/PowerToysOverview_ShortcutGuide) |
|
||||
| [Text Extractor](https://aka.ms/PowerToysOverview_TextExtractor) | [Video Conference Mute](https://aka.ms/PowerToysOverview_VideoConference) | [Workspaces](https://aka.ms/PowerToysOverview_Workspaces) |
|
||||
|
||||
## 🎁⭐ PowerToys Advent calendar ⭐🎁
|
||||
|
||||
We will be highlighting a cool utility each day for 24 days in December! To follow along, check out these threads:
|
||||
- https://bsky.app/profile/kaylacinnamon.bsky.social/post/3lcb7iljxck2o
|
||||
- https://x.com/cinnamon_msft/status/1863284610773246257
|
||||
|
||||
## Installing and running Microsoft PowerToys
|
||||
|
||||
### Requirements
|
||||
|
||||
@@ -47,7 +47,7 @@ registerAdditionalNewLanguage("id", [".fileExtension"], idDefinition(), monaco)
|
||||
|
||||
* The id can be anything. Recommended is one of the file extensions. For example "php" or "reg".
|
||||
|
||||
4. In case you wish to add a custom color for a token, you can do so by adding the following line to [`customTokenColors.js`](/src/Monaco/customTokenColors.js):
|
||||
4. In case you wish to add a custom color for a token, you can do so by adding the following line to [`customTokenThemeRules.js`](/src/Monaco/customTokenThemeRules.js):
|
||||
```javascript
|
||||
{token: 'token-name', foreground: 'ff0000'}
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Value Generator Plugin
|
||||
|
||||
The Value Generator plugin is used to generate hashes for strings, to calculate base64 encodings, escape and encode URLs/URIs and to generate GUIDs versions 1, 3, 4 and 5.
|
||||
The Value Generator plugin is used to generate hashes for strings, to calculate base64 encodings, escape and encode URLs/URIs and to generate GUIDs of version 1, 3, 4, 5, and 7.
|
||||
|
||||

|
||||
|
||||
@@ -34,7 +34,10 @@ The Value Generator plugin is used to generate hashes for strings, to calculate
|
||||
|
||||
### [`GUIDGenerator`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDGenerator.cs)
|
||||
- Utility class for generating or calculating GUIDs
|
||||
- Generating GUID versions 1 and 4 is done using builtin APIs. [`UuidCreateSequential`](https://learn.microsoft.com/en-us/windows/win32/api/rpcdce/nf-rpcdce-uuidcreatesequential) for version 1 and `System.Guid.NewGuid()` for version 4
|
||||
- Generating GUID versions 1, 4, and 7 is done using builtin APIs:
|
||||
- [`UuidCreateSequential`](https://learn.microsoft.com/en-us/windows/win32/api/rpcdce/nf-rpcdce-uuidcreatesequential) for version 1
|
||||
- `System.Guid.NewGuid()` for version 4
|
||||
- `System.Guid.CreateVersion7()` for version 7
|
||||
- Versions 3 and 5 take two parameters, a namespace and a name
|
||||
- The namespace must be a valid GUID or one of the [predefined ones](https://datatracker.ietf.org/doc/html/rfc4122#appendix-C)
|
||||
- The `PredefinedNamespaces` dictionary contains aliases for the predefined namespaces
|
||||
|
||||
BIN
doc/images/overview/PT_holiday_hero_image.png
Normal file
BIN
doc/images/overview/PT_holiday_hero_image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 503 KiB |
@@ -30,7 +30,7 @@ Function Generate-FileList() {
|
||||
|
||||
$fileExclusionList = @("*.pdb", "*.lastcodeanalysissucceeded", "createdump.exe", "powertoys.exe")
|
||||
|
||||
$fileInclusionList = @("*.dll", "*.exe", "*.json", "*.msix", "*.png", "*.gif", "*.ico", "*.cur", "*.svg", "index.html", "reg.js", "gitignore.js", "monacoSpecialLanguages.js", "customTokenColors.js", "*.pri")
|
||||
$fileInclusionList = @("*.dll", "*.exe", "*.json", "*.msix", "*.png", "*.gif", "*.ico", "*.cur", "*.svg", "index.html", "reg.js", "gitignore.js", "srt.js", "monacoSpecialLanguages.js", "customTokenThemeRules.js", "*.pri")
|
||||
|
||||
$dllsToIgnore = @("System.CodeDom.dll", "WindowsBase.dll")
|
||||
|
||||
|
||||
@@ -46,32 +46,28 @@ dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
|
||||
|
||||
dotnet_naming_symbols.interface.applicable_kinds = interface
|
||||
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.interface.required_modifiers =
|
||||
dotnet_naming_symbols.interface.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
|
||||
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.types.required_modifiers =
|
||||
dotnet_naming_symbols.types.required_modifiers =
|
||||
|
||||
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
||||
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||
|
||||
# Naming styles
|
||||
|
||||
dotnet_naming_style.begins_with_i.required_prefix = I
|
||||
dotnet_naming_style.begins_with_i.required_suffix =
|
||||
dotnet_naming_style.begins_with_i.word_separator =
|
||||
dotnet_naming_style.begins_with_i.required_suffix =
|
||||
dotnet_naming_style.begins_with_i.word_separator =
|
||||
dotnet_naming_style.begins_with_i.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.pascal_case.required_prefix =
|
||||
dotnet_naming_style.pascal_case.required_suffix =
|
||||
dotnet_naming_style.pascal_case.word_separator =
|
||||
dotnet_naming_style.pascal_case.required_prefix =
|
||||
dotnet_naming_style.pascal_case.required_suffix =
|
||||
dotnet_naming_style.pascal_case.word_separator =
|
||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||
|
||||
dotnet_naming_style.pascal_case.required_prefix =
|
||||
dotnet_naming_style.pascal_case.required_suffix =
|
||||
dotnet_naming_style.pascal_case.word_separator =
|
||||
dotnet_naming_style.pascal_case.capitalization = pascal_case
|
||||
dotnet_style_coalesce_expression = true:suggestion
|
||||
dotnet_style_null_propagation = true:suggestion
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
|
||||
@@ -96,4 +92,176 @@ end_of_line = crlf
|
||||
dotnet_diagnostic.IDE0065.severity = none
|
||||
|
||||
# IDE0009: Add this or Me qualification
|
||||
dotnet_diagnostic.IDE0009.severity = none
|
||||
dotnet_diagnostic.IDE0009.severity = none
|
||||
|
||||
# IDE-based code analysis rules
|
||||
# IDE0005: Remove unnecessary import
|
||||
dotnet_diagnostic.IDE0005.severity = suggestion
|
||||
|
||||
# IDE0008: Use explicit type instead of 'var'
|
||||
dotnet_diagnostic.IDE0008.severity = silent
|
||||
|
||||
# IDE0016: Use throw expression
|
||||
dotnet_diagnostic.IDE0016.severity = suggestion
|
||||
|
||||
# IDE0018: Inline variable declaration
|
||||
dotnet_diagnostic.IDE0018.severity = suggestion
|
||||
|
||||
# IDE0019: Use pattern matching
|
||||
dotnet_diagnostic.IDE0019.severity = suggestion
|
||||
|
||||
# IDE0021: Use expression body for constructors
|
||||
dotnet_diagnostic.IDE0021.severity = silent
|
||||
|
||||
# IDE0022: Use expression body for methods
|
||||
dotnet_diagnostic.IDE0022.severity = silent
|
||||
|
||||
# IDE0023: Use expression body for conversion operators
|
||||
dotnet_diagnostic.IDE0023.severity = silent
|
||||
|
||||
# IDE0025: Use expression body for properties
|
||||
dotnet_diagnostic.IDE0025.severity = silent
|
||||
|
||||
# IDE0027: Use expression body for accessors
|
||||
dotnet_diagnostic.IDE0027.severity = silent
|
||||
|
||||
# IDE0028: Use collection initializers
|
||||
dotnet_diagnostic.IDE0028.severity = suggestion
|
||||
|
||||
# IDE0029: Null check can be simplified
|
||||
dotnet_diagnostic.IDE0029.severity = suggestion
|
||||
|
||||
# IDE0031: Use null propagation
|
||||
dotnet_diagnostic.IDE0031.severity = suggestion
|
||||
|
||||
# IDE0032: Use auto property
|
||||
dotnet_diagnostic.IDE0032.severity = suggestion
|
||||
|
||||
# IDE0034: Simplify default expression
|
||||
dotnet_diagnostic.IDE0034.severity = suggestion
|
||||
|
||||
# IDE0036: Order modifiers
|
||||
dotnet_diagnostic.IDE0036.severity = suggestion
|
||||
|
||||
# IDE0039: Use local function instead of lambda
|
||||
dotnet_diagnostic.IDE0039.severity = suggestion
|
||||
|
||||
# IDE0042: Deconstruct variable declaration
|
||||
dotnet_diagnostic.IDE0042.severity = suggestion
|
||||
|
||||
# IDE0044: Add readonly modifier
|
||||
dotnet_diagnostic.IDE0044.severity = suggestion
|
||||
|
||||
# IDE0045: Use conditional expression for assignment
|
||||
dotnet_diagnostic.IDE0045.severity = suggestion
|
||||
|
||||
# IDE0046: Use conditional expression for return
|
||||
dotnet_diagnostic.IDE0046.severity = suggestion
|
||||
|
||||
# IDE0047: Remove unnecessary parentheses
|
||||
dotnet_diagnostic.IDE0047.severity = suggestion
|
||||
|
||||
# IDE0051: Remove unused private member
|
||||
dotnet_diagnostic.IDE0051.severity = suggestion
|
||||
|
||||
# IDE0052: Remove unread private member
|
||||
dotnet_diagnostic.IDE0052.severity = suggestion
|
||||
|
||||
# IDE0054: Use compound assignment
|
||||
dotnet_diagnostic.IDE0054.severity = suggestion
|
||||
|
||||
# IDE0055: Fix formatting
|
||||
dotnet_diagnostic.IDE0055.severity = suggestion
|
||||
|
||||
# IDE0056: Use index operator
|
||||
dotnet_diagnostic.IDE0056.severity = suggestion
|
||||
|
||||
# IDE0057: Use range operator
|
||||
dotnet_diagnostic.IDE0057.severity = suggestion
|
||||
|
||||
# IDE0059: Remove unnecessary value assignment
|
||||
dotnet_diagnostic.IDE0059.severity = suggestion
|
||||
|
||||
# IDE0060: Remove unused parameter
|
||||
dotnet_diagnostic.IDE0060.severity = suggestion
|
||||
|
||||
# IDE0061: Use expression body for local functions
|
||||
dotnet_diagnostic.IDE0061.severity = silent
|
||||
|
||||
# IDE0063: Use simple 'using' statement
|
||||
dotnet_diagnostic.IDE0063.severity = suggestion
|
||||
|
||||
# IDE0071: Simplify interpolation
|
||||
dotnet_diagnostic.IDE0071.severity = suggestion
|
||||
|
||||
# IDE0074: Use coalesce compound assignment
|
||||
dotnet_diagnostic.IDE0074.severity = suggestion
|
||||
|
||||
# IDE0075: Simplify conditional expression
|
||||
dotnet_diagnostic.IDE0075.severity = suggestion
|
||||
|
||||
# IDE0077: Avoid legacy format target in global 'SuppressMessageAttribute'
|
||||
dotnet_diagnostic.IDE0077.severity = suggestion
|
||||
|
||||
# IDE0078: Use pattern matching
|
||||
dotnet_diagnostic.IDE0078.severity = suggestion
|
||||
|
||||
# IDE0083: Use pattern matching ('not' operator)
|
||||
dotnet_diagnostic.IDE0083.severity = suggestion
|
||||
|
||||
# IDE0090: Simplify 'new' expression
|
||||
dotnet_diagnostic.IDE0090.severity = suggestion
|
||||
|
||||
# IDE0100: Remove unnecessary equality operator
|
||||
dotnet_diagnostic.IDE0100.severity = suggestion
|
||||
|
||||
# IDE0130: Namespace does not match folder structure
|
||||
dotnet_diagnostic.IDE0130.severity = suggestion
|
||||
|
||||
# IDE0160: Use block-scoped namespace
|
||||
dotnet_diagnostic.IDE0160.severity = silent
|
||||
|
||||
# IDE0180: Use tuple to swap values
|
||||
dotnet_diagnostic.IDE0180.severity = suggestion
|
||||
|
||||
# IDE0200: Remove unnecessary lambda expression
|
||||
dotnet_diagnostic.IDE0200.severity = suggestion
|
||||
|
||||
# IDE0240: Nullable directive is redundant
|
||||
dotnet_diagnostic.IDE0240.severity = suggestion
|
||||
|
||||
# IDE0250: Struct can be made 'readonly'
|
||||
dotnet_diagnostic.IDE0250.severity = suggestion
|
||||
|
||||
# IDE0251: Member can be made 'readonly''
|
||||
dotnet_diagnostic.IDE0251.severity = suggestion
|
||||
|
||||
# IDE0260: Use pattern matching
|
||||
dotnet_diagnostic.IDE0260.severity = suggestion
|
||||
|
||||
# IDE0270: Null check can be simplified
|
||||
dotnet_diagnostic.IDE0270.severity = suggestion
|
||||
|
||||
# IDE0290: Use primary constructor
|
||||
dotnet_diagnostic.IDE0290.severity = silent
|
||||
|
||||
# IDE0300: Use collection expression for array
|
||||
dotnet_diagnostic.IDE0300.severity = suggestion
|
||||
|
||||
# IDE0301: Use collection expression for empty
|
||||
dotnet_diagnostic.IDE0301.severity = suggestion
|
||||
|
||||
# IDE0305: Use collection expression for fluent
|
||||
dotnet_diagnostic.IDE0305.severity = suggestion
|
||||
|
||||
# IDE1005: Use conditional delegate call
|
||||
dotnet_diagnostic.IDE1005.severity = suggestion
|
||||
|
||||
# CA1859: Use concrete types when possible for improved performance
|
||||
dotnet_diagnostic.CA1859.severity = suggestion
|
||||
|
||||
# CA2202: Avoid inexact read with Stream.Read
|
||||
dotnet_diagnostic.CA2022.severity = suggestion
|
||||
|
||||
# CA2263: Prefer generic overload when type is known
|
||||
dotnet_diagnostic.CA2263.severity = suggestion
|
||||
|
||||
9
src/Common.Dotnet.AotCompatibility.props
Normal file
9
src/Common.Dotnet.AotCompatibility.props
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Some items may be set in Directory.Build.props in root -->
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<IsAotCompatible>true</IsAotCompatible>
|
||||
<CsWinRTAotOptimizerEnabled>true</CsWinRTAotOptimizerEnabled>
|
||||
<CsWinRTAotWarningLevel>2</CsWinRTAotWarningLevel>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -2,8 +2,8 @@
|
||||
<!-- Include Monaco Editor source code -->
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<None Include="$(MSBuildThisFileDirectory)\Monaco\customTokenColors.js">
|
||||
<Link>Assets\Monaco\customTokenColors.js</Link>
|
||||
<None Include="$(MSBuildThisFileDirectory)\Monaco\customTokenThemeRules.js">
|
||||
<Link>Assets\Monaco\customTokenThemeRules.js</Link>
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="$(MSBuildThisFileDirectory)\Monaco\monacoSpecialLanguages.js">
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
root: [
|
||||
[/^#.*$/, 'comment'],
|
||||
[/.*((?<!(^|\/))\*\*.*|\*\*(?!(\/|$))).*/, 'invalid'],
|
||||
[/((?:^!\s*(?:\\\s|\S)+)?)((?:^\s*(?:\\\s|\S)+)?)((?:\s+(?:\\\s|\S)+)*)/, ['custom-gitignore.negation', 'tag', 'invalid']]
|
||||
[/((?:^!\s*(?:\\\s|\S)+)?)((?:^\s*(?:\\\s|\S)+)?)((?:\s+(?:\\\s|\S)+)*)/, ['custom-negation', 'tag', 'invalid']]
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
29
src/Monaco/customLanguages/srt.js
Normal file
29
src/Monaco/customLanguages/srt.js
Normal file
@@ -0,0 +1,29 @@
|
||||
export function srtDefinition() {
|
||||
return {
|
||||
tokenizer: {
|
||||
root: [
|
||||
[/\s*\d+/, 'number', '@block']
|
||||
],
|
||||
|
||||
block: [
|
||||
[/^\d{2}:\d{2}:\d{2},\d{3} --> \d{2}:\d{2}:\d{2},\d{3}/, {
|
||||
cases: {
|
||||
'@eos': {token: 'type.identifier', next: '@subtitle'},
|
||||
'@default': {token: 'type.identifier', next: '@ignore'}
|
||||
}
|
||||
}],
|
||||
[/^$/, 'string', '@pop']
|
||||
],
|
||||
|
||||
ignore: [
|
||||
[/.+$/, '', '@subtitle']
|
||||
],
|
||||
|
||||
subtitle: [
|
||||
[/^$/, 'string', '@popall'],
|
||||
[/<\/?(?:[ibu]|font(?:\s+color="[^"]+"\s*)?)>/, 'tag'],
|
||||
[/./, 'string']
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
export const customTokenColors = [
|
||||
{token: 'custom-gitignore.negation', foreground: 'c00ce0'}
|
||||
];
|
||||
3
src/Monaco/customTokenThemeRules.js
Normal file
3
src/Monaco/customTokenThemeRules.js
Normal file
@@ -0,0 +1,3 @@
|
||||
export const customTokenThemeRules = [
|
||||
{token: 'custom-negation.gitignore', foreground: 'c00ce0'}
|
||||
];
|
||||
@@ -79,7 +79,7 @@
|
||||
<script src="http://[[PT_URL]]/monacoSpecialLanguages.js" type="module"></script>
|
||||
<script type="module">
|
||||
import { registerAdditionalLanguages } from 'http://[[PT_URL]]/monacoSpecialLanguages.js';
|
||||
import { customTokenColors } from 'http://[[PT_URL]]/customTokenColors.js';
|
||||
import { customTokenThemeRules } from 'http://[[PT_URL]]/customTokenThemeRules.js';
|
||||
require.config({ paths: { vs: 'http://[[PT_URL]]/monacoSRC/min/vs' } });
|
||||
require(['vs/editor/editor.main'], async function () {
|
||||
await registerAdditionalLanguages(monaco)
|
||||
@@ -88,7 +88,7 @@
|
||||
monaco.editor.defineTheme('theme', {
|
||||
base: theme, // Sets the base theme to "vs" or "vs-dark" depending on the user's preference
|
||||
inherit: true,
|
||||
rules: customTokenColors,
|
||||
rules: customTokenThemeRules,
|
||||
colors: {} // `colors` is a required attribute
|
||||
});
|
||||
|
||||
|
||||
@@ -2,18 +2,20 @@
|
||||
|
||||
import { regDefinition } from './customLanguages/reg.js';
|
||||
import { gitignoreDefinition } from './customLanguages/gitignore.js';
|
||||
import { srtDefinition } from './customLanguages/srt.js';
|
||||
|
||||
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("razorExt", [".razor"], "razor", monaco)
|
||||
registerAdditionalLanguage("vbExt", [".vbs"], "vb", monaco)
|
||||
registerAdditionalLanguage("iniExt", [".inf", ".gitconfig", ".gitattributes", ".editorconfig"], "ini", monaco)
|
||||
registerAdditionalLanguage("shellExt", [".ksh", ".zsh", ".bsh"], "shell", monaco)
|
||||
registerAdditionalNewLanguage("reg", [".reg"], regDefinition(), monaco)
|
||||
registerAdditionalNewLanguage("gitignore", [".gitignore"], gitignoreDefinition(), monaco)
|
||||
registerAdditionalLanguage("cppExt", [".ino", ".pde"], "cpp", monaco);
|
||||
registerAdditionalLanguage("xmlExt", [".wsdl", ".csproj", ".vcxproj", ".vbproj", ".fsproj"], "xml", monaco);
|
||||
registerAdditionalLanguage("txtExt", [".sln", ".log", ".vsconfig", ".env", ".ahk", ".ion"], "txt", monaco);
|
||||
registerAdditionalLanguage("razorExt", [".razor"], "razor", monaco);
|
||||
registerAdditionalLanguage("vbExt", [".vbs"], "vb", monaco);
|
||||
registerAdditionalLanguage("iniExt", [".inf", ".gitconfig", ".gitattributes", ".editorconfig"], "ini", monaco);
|
||||
registerAdditionalLanguage("shellExt", [".ksh", ".zsh", ".bsh"], "shell", monaco);
|
||||
registerAdditionalNewLanguage("reg", [".reg"], regDefinition(), monaco);
|
||||
registerAdditionalNewLanguage("gitignore", [".gitignore"], gitignoreDefinition(), monaco);
|
||||
registerAdditionalNewLanguage("srt", [".srt"], srtDefinition(), monaco);
|
||||
}
|
||||
|
||||
// Language definitions taken from Monaco source code
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -109,6 +109,24 @@ namespace DPIAware
|
||||
}
|
||||
}
|
||||
|
||||
void InverseConvert(HMONITOR monitor_handle, RECT& rect)
|
||||
{
|
||||
if (monitor_handle == NULL)
|
||||
{
|
||||
const POINT ptZero = { 0, 0 };
|
||||
monitor_handle = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY);
|
||||
}
|
||||
|
||||
UINT dpi_x, dpi_y;
|
||||
if (GetDpiForMonitor(monitor_handle, MDT_EFFECTIVE_DPI, &dpi_x, &dpi_y) == S_OK)
|
||||
{
|
||||
rect.left = static_cast<long>(std::round(rect.left * static_cast<float>(DEFAULT_DPI) / dpi_x));
|
||||
rect.right = static_cast<long>(std::round(rect.right * static_cast<float>(DEFAULT_DPI) / dpi_x));
|
||||
rect.top = static_cast<long>(std::round(rect.top * static_cast<float>(DEFAULT_DPI) / dpi_y));
|
||||
rect.bottom = static_cast<long>(std::round(rect.bottom * static_cast<float>(DEFAULT_DPI) / dpi_y));
|
||||
}
|
||||
}
|
||||
|
||||
void EnableDPIAwarenessForThisProcess()
|
||||
{
|
||||
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace DPIAware
|
||||
void Convert(HMONITOR monitor_handle, RECT& rect);
|
||||
void ConvertByCursorPosition(float& width, float& height);
|
||||
void InverseConvert(HMONITOR monitor_handle, float& width, float& height);
|
||||
void InverseConvert(HMONITOR monitor_handle, RECT& rect);
|
||||
void EnableDPIAwarenessForThisProcess();
|
||||
|
||||
enum AwarenessLevel
|
||||
|
||||
@@ -241,10 +241,12 @@ void LayoutMap::LayoutMapImpl::UpdateLayout()
|
||||
keyboardLayoutMap[VK_KANA] = L"IME Kana";
|
||||
keyboardLayoutMap[VK_HANGEUL] = L"IME Hangeul";
|
||||
keyboardLayoutMap[VK_HANGUL] = L"IME Hangul";
|
||||
keyboardLayoutMap[VK_IME_ON] = L"IME On";
|
||||
keyboardLayoutMap[VK_JUNJA] = L"IME Junja";
|
||||
keyboardLayoutMap[VK_FINAL] = L"IME Final";
|
||||
keyboardLayoutMap[VK_HANJA] = L"IME Hanja";
|
||||
keyboardLayoutMap[VK_KANJI] = L"IME Kanji";
|
||||
keyboardLayoutMap[VK_IME_OFF] = L"IME Off";
|
||||
keyboardLayoutMap[VK_CONVERT] = L"IME Convert";
|
||||
keyboardLayoutMap[VK_NONCONVERT] = L"IME Non-Convert";
|
||||
keyboardLayoutMap[VK_ACCEPT] = L"IME Kana";
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)\tests\AdvancedPaste.FuzzTests\</OutputPath>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\AdvancedPaste\Helpers\JsonHelper.cs" Link="JsonHelper.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="MSTest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Microsoft.VisualStudio.TestTools.UnitTesting" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="OneFuzzConfig.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,19 @@
|
||||
// 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 AdvancedPaste.Helpers;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
|
||||
namespace AdvancedPaste.FuzzTests
|
||||
{
|
||||
public class FuzzTests
|
||||
{
|
||||
public static void FuzzToJsonFromXmlOrCsv(ReadOnlySpan<byte> input)
|
||||
{
|
||||
var dataPackage = new DataPackage();
|
||||
dataPackage.SetText(input.ToString());
|
||||
JsonHelper.ToJsonFromXmlOrCsv(dataPackage.GetView());
|
||||
}
|
||||
}
|
||||
}
|
||||
44
src/modules/AdvancedPaste/AdvancedPaste.FuzzTests/Logger.cs
Normal file
44
src/modules/AdvancedPaste/AdvancedPaste.FuzzTests/Logger.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ManagedCommon
|
||||
{
|
||||
public static class Logger
|
||||
{
|
||||
// An empty method to simulate logging information
|
||||
public static void LogTrace()
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
// An empty method to simulate logging information
|
||||
public static void LogInfo(string message)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
// An empty method to simulate logging warnings
|
||||
public static void LogWarning(string message)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
// An empty method to simulate logging errors
|
||||
public static void LogError(string message, Exception? ex = null)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public static void LogDebug(string message, Exception? ex = null)
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// 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.
|
||||
|
||||
[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"configVersion": 3,
|
||||
"entries": [
|
||||
{
|
||||
"fuzzer": {
|
||||
"$type": "libfuzzerDotNet",
|
||||
"dll": "AdvancedPaste.FuzzTests.dll",
|
||||
"class": "AdvancedPaste.FuzzTests.FuzzTests",
|
||||
"method": "FuzzPhoneNumber"
|
||||
},
|
||||
"adoTemplate": {
|
||||
// supply the values appropriate to your
|
||||
// project, where bugs will be filed
|
||||
"org": "microsoft",
|
||||
"project": "OS",
|
||||
"AssignedTo": "leilzh@microsoft.com",
|
||||
"AreaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\SHINE\\PowerToys",
|
||||
"IterationPath": "OS\\Future"
|
||||
},
|
||||
"jobNotificationEmail": "leilzh@microsoft.com",
|
||||
"skip": false,
|
||||
"rebootAfterSetup": false,
|
||||
"oneFuzzJobs": [
|
||||
// at least one job is required
|
||||
{
|
||||
"projectName": "AdvancedPaste",
|
||||
"targetName": "AdvancedPaste-dotnet-fuzzer"
|
||||
}
|
||||
],
|
||||
"jobDependencies": [
|
||||
// this should contain, at minimum,
|
||||
// the DLL and PDB files
|
||||
// you will need to add any other files required
|
||||
// (globs are supported)
|
||||
"AdvancedPaste.FuzzTests.dll",
|
||||
"AdvancedPaste.FuzzTests.pdb"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -33,6 +33,7 @@ using Timer = System.Windows.Forms.Timer;
|
||||
[module: SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", Scope = "member", Target = "MouseWithoutBorders.frmMatrix.#Dispose(System.Boolean)", MessageId = "logoBitmap", Justification = "Dotnet port with style preservation")]
|
||||
[module: SuppressMessage("Microsoft.Mobility", "CA1601:DoNotUseTimersThatPreventPowerStateChanges", Scope = "member", Target = "MouseWithoutBorders.frmMatrix.#frmMatrix_Shown(System.Object,System.EventArgs)", Justification = "Dotnet port with style preservation")]
|
||||
[module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.frmMatrix.#PaintMyLogo()", Justification = "Dotnet port with style preservation")]
|
||||
[module: SuppressMessage("Style", "IDE1006:Naming Styles", Scope = "member", Target = "~M:MouseWithoutBorders.FrmMatrix.M_EnabledChanged(System.Object,System.EventArgs)", Justification = "Dotnet port with style preservation")]
|
||||
|
||||
namespace MouseWithoutBorders
|
||||
{
|
||||
|
||||
@@ -215,10 +215,10 @@ namespace newplus::utilities
|
||||
|
||||
inline bool is_desktop_folder(const std::filesystem::path target_fullpath)
|
||||
{
|
||||
TCHAR desktopPath[MAX_PATH];
|
||||
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, desktopPath)))
|
||||
TCHAR desktop_path[MAX_PATH];
|
||||
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, desktop_path)))
|
||||
{
|
||||
return StrCmpIW(target_fullpath.c_str(), desktopPath) == 0;
|
||||
return StrCmpIW(target_fullpath.c_str(), desktop_path) == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -376,8 +376,10 @@ namespace newplus::utilities
|
||||
// Copy file and determine final filename
|
||||
std::filesystem::path target_final_fullpath = template_entry->copy_object_to(GetActiveWindow(), target_fullpath);
|
||||
|
||||
// Consider copy completed. If we do tracing after enter_rename_mode, then rename mode won't consistently work
|
||||
trace.UpdateState(true);
|
||||
Trace::EventCopyTemplate(target_final_fullpath.extension().c_str());
|
||||
Trace::EventCopyTemplateResult(hr);
|
||||
trace.Flush();
|
||||
trace.UpdateState(false);
|
||||
|
||||
@@ -392,13 +394,12 @@ namespace newplus::utilities
|
||||
Logger::error(ex.what());
|
||||
|
||||
hr = S_FALSE;
|
||||
trace.UpdateState(true);
|
||||
Trace::EventCopyTemplateResult(hr);
|
||||
trace.Flush();
|
||||
trace.UpdateState(false);
|
||||
}
|
||||
|
||||
trace.UpdateState(true);
|
||||
Trace::EventCopyTemplateResult(hr);
|
||||
trace.Flush();
|
||||
trace.UpdateState(false);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,6 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Workspaces.Data;
|
||||
|
||||
using static WorkspacesEditor.Data.ProjectData;
|
||||
|
||||
namespace WorkspacesEditor.Data
|
||||
|
||||
@@ -8,13 +8,7 @@ namespace WorkspacesEditor.Data
|
||||
{
|
||||
public class TempProjectData : ProjectData
|
||||
{
|
||||
public static string File
|
||||
{
|
||||
get
|
||||
{
|
||||
return FolderUtils.DataFolder() + "\\temp-workspaces.json";
|
||||
}
|
||||
}
|
||||
public static string File => FolderUtils.DataFolder() + "\\temp-workspaces.json";
|
||||
|
||||
public static void DeleteTempFile()
|
||||
{
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Workspaces.Data;
|
||||
using WorkspacesEditor.Utils;
|
||||
|
||||
using static WorkspacesEditor.Data.ProjectData;
|
||||
@@ -14,13 +12,7 @@ namespace WorkspacesEditor.Data
|
||||
{
|
||||
public class WorkspacesData : WorkspacesEditorData<WorkspacesListWrapper>
|
||||
{
|
||||
public string File
|
||||
{
|
||||
get
|
||||
{
|
||||
return FolderUtils.DataFolder() + "\\workspaces.json";
|
||||
}
|
||||
}
|
||||
public string File => FolderUtils.DataFolder() + "\\workspaces.json";
|
||||
|
||||
public struct WorkspacesListWrapper
|
||||
{
|
||||
|
||||
@@ -3,28 +3,24 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Text.Json;
|
||||
|
||||
using WorkspacesEditor.Utils;
|
||||
|
||||
namespace Workspaces.Data
|
||||
namespace WorkspacesEditor.Data
|
||||
{
|
||||
public class WorkspacesEditorData<T>
|
||||
{
|
||||
protected JsonSerializerOptions JsonOptions
|
||||
{
|
||||
get
|
||||
get => new()
|
||||
{
|
||||
return new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = new DashCaseNamingPolicy(),
|
||||
WriteIndented = true,
|
||||
};
|
||||
}
|
||||
PropertyNamingPolicy = new DashCaseNamingPolicy(),
|
||||
WriteIndented = true,
|
||||
};
|
||||
}
|
||||
|
||||
public T Read(string file)
|
||||
{
|
||||
IOUtils ioUtils = new IOUtils();
|
||||
IOUtils ioUtils = new();
|
||||
string data = ioUtils.ReadFile(file);
|
||||
return JsonSerializer.Deserialize<T>(data, JsonOptions);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
xmlns:ui="http://schemas.modernwpf.com/2019"
|
||||
x:Name="WorkspacesMainWindow"
|
||||
Title="{x:Static props:Resources.MainTitle}"
|
||||
MinWidth="700"
|
||||
MinWidth="750"
|
||||
MinHeight="680"
|
||||
ui:TitleBar.Background="{DynamicResource PrimaryBackgroundBrush}"
|
||||
ui:TitleBar.InactiveBackground="{DynamicResource TertiaryBackgroundBrush}"
|
||||
|
||||
@@ -18,14 +18,7 @@ namespace WorkspacesEditor.Models
|
||||
|
||||
public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
|
||||
{
|
||||
if (item is MonitorHeaderRow)
|
||||
{
|
||||
return HeaderTemplate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AppTemplate;
|
||||
}
|
||||
return item is MonitorHeaderRow ? HeaderTemplate : AppTemplate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,22 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
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 enum WindowPositionKind
|
||||
{
|
||||
Custom = 0,
|
||||
Maximized = 1,
|
||||
Minimized = 2,
|
||||
}
|
||||
|
||||
public class Application : BaseApplication, IDisposable
|
||||
{
|
||||
private bool _isInitialized;
|
||||
@@ -79,7 +76,7 @@ namespace WorkspacesEditor.Models
|
||||
return left.X != right.X || left.Y != right.Y || left.Width != right.Width || left.Height != right.Height;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
public override readonly bool Equals(object obj)
|
||||
{
|
||||
if (obj == null || GetType() != obj.GetType())
|
||||
{
|
||||
@@ -90,7 +87,7 @@ namespace WorkspacesEditor.Models
|
||||
return X == pos.X && Y == pos.Y && Width == pos.Width && Height == pos.Height;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
public override readonly int GetHashCode()
|
||||
{
|
||||
return base.GetHashCode();
|
||||
}
|
||||
@@ -136,36 +133,24 @@ namespace WorkspacesEditor.Models
|
||||
}
|
||||
}
|
||||
|
||||
private bool _minimized;
|
||||
public bool Minimized { get; set; }
|
||||
|
||||
public bool Minimized
|
||||
public bool Maximized { get; set; }
|
||||
|
||||
public bool EditPositionEnabled => !Minimized && !Maximized;
|
||||
|
||||
public int PositionComboboxIndex
|
||||
{
|
||||
get => _minimized;
|
||||
get => Maximized ? (int)WindowPositionKind.Maximized : Minimized ? (int)WindowPositionKind.Minimized : (int)WindowPositionKind.Custom;
|
||||
set
|
||||
{
|
||||
_minimized = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(Minimized)));
|
||||
Maximized = value == (int)WindowPositionKind.Maximized;
|
||||
Minimized = value == (int)WindowPositionKind.Minimized;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(EditPositionEnabled)));
|
||||
RedrawPreviewImage();
|
||||
}
|
||||
}
|
||||
|
||||
private bool _maximized;
|
||||
|
||||
public bool Maximized
|
||||
{
|
||||
get => _maximized;
|
||||
set
|
||||
{
|
||||
_maximized = value;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(Maximized)));
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(EditPositionEnabled)));
|
||||
RedrawPreviewImage();
|
||||
}
|
||||
}
|
||||
|
||||
public bool EditPositionEnabled { get => !Minimized && !Maximized; }
|
||||
|
||||
private string _appMainParams;
|
||||
|
||||
public string AppMainParams
|
||||
@@ -183,7 +168,7 @@ namespace WorkspacesEditor.Models
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsAppMainParamVisible { get => !string.IsNullOrWhiteSpace(_appMainParams); }
|
||||
public bool IsAppMainParamVisible => !string.IsNullOrWhiteSpace(_appMainParams);
|
||||
|
||||
[JsonIgnore]
|
||||
public bool IsHighlighted { get; set; }
|
||||
@@ -192,13 +177,7 @@ namespace WorkspacesEditor.Models
|
||||
public int RepeatIndex { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public string RepeatIndexString
|
||||
{
|
||||
get
|
||||
{
|
||||
return RepeatIndex <= 1 ? string.Empty : RepeatIndex.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
public string RepeatIndexString => RepeatIndex <= 1 ? string.Empty : RepeatIndex.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
private WindowPosition _position;
|
||||
|
||||
@@ -242,10 +221,7 @@ namespace WorkspacesEditor.Models
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_monitorSetup == null)
|
||||
{
|
||||
_monitorSetup = Parent.GetMonitorForApp(this);
|
||||
}
|
||||
_monitorSetup ??= Parent.GetMonitorForApp(this);
|
||||
|
||||
return _monitorSetup;
|
||||
}
|
||||
@@ -271,7 +247,7 @@ namespace WorkspacesEditor.Models
|
||||
}
|
||||
}
|
||||
|
||||
public string DeleteButtonContent { get => _isIncluded ? Properties.Resources.Delete : Properties.Resources.AddBack; }
|
||||
public string DeleteButtonContent => _isIncluded ? Properties.Resources.Delete : Properties.Resources.AddBack;
|
||||
|
||||
private bool _isIncluded = true;
|
||||
|
||||
@@ -298,15 +274,5 @@ namespace WorkspacesEditor.Models
|
||||
CommandLineArguments = newCommandLineValue;
|
||||
OnPropertyChanged(new PropertyChangedEventArgs(nameof(AppMainParams)));
|
||||
}
|
||||
|
||||
internal void MaximizedChecked()
|
||||
{
|
||||
Minimized = false;
|
||||
}
|
||||
|
||||
internal void MinimizedChecked()
|
||||
{
|
||||
Maximized = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,28 +6,18 @@ using System.Windows;
|
||||
|
||||
namespace WorkspacesEditor.Models
|
||||
{
|
||||
public class Monitor
|
||||
public class Monitor(string monitorName, string monitorInstanceId, int number, int dpi, Rect dpiAwareBounds, Rect dpiUnawareBounds)
|
||||
{
|
||||
public string MonitorName { get; private set; }
|
||||
public string MonitorName { get; private set; } = monitorName;
|
||||
|
||||
public string MonitorInstanceId { get; private set; }
|
||||
public string MonitorInstanceId { get; private set; } = monitorInstanceId;
|
||||
|
||||
public int MonitorNumber { get; private set; }
|
||||
public int MonitorNumber { get; private set; } = number;
|
||||
|
||||
public int Dpi { get; private set; }
|
||||
public int Dpi { get; private set; } = dpi;
|
||||
|
||||
public Rect MonitorDpiUnawareBounds { get; private set; }
|
||||
public Rect MonitorDpiUnawareBounds { get; private set; } = dpiUnawareBounds;
|
||||
|
||||
public Rect MonitorDpiAwareBounds { get; private set; }
|
||||
|
||||
public Monitor(string monitorName, string monitorInstanceId, int number, int dpi, Rect dpiAwareBounds, Rect dpiUnawareBounds)
|
||||
{
|
||||
MonitorName = monitorName;
|
||||
MonitorInstanceId = monitorInstanceId;
|
||||
MonitorNumber = number;
|
||||
Dpi = dpi;
|
||||
MonitorDpiAwareBounds = dpiAwareBounds;
|
||||
MonitorDpiUnawareBounds = dpiUnawareBounds;
|
||||
}
|
||||
public Rect MonitorDpiAwareBounds { get; private set; } = dpiAwareBounds;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,9 +16,9 @@ namespace WorkspacesEditor.Models
|
||||
PropertyChanged?.Invoke(this, e);
|
||||
}
|
||||
|
||||
public string MonitorInfo { get => MonitorName; }
|
||||
public string MonitorInfo => MonitorName;
|
||||
|
||||
public string MonitorInfoWithResolution { get => $"{MonitorName} {MonitorDpiAwareBounds.Width}x{MonitorDpiAwareBounds.Height}"; }
|
||||
public string MonitorInfoWithResolution => $"{MonitorName} {MonitorDpiAwareBounds.Width}x{MonitorDpiAwareBounds.Height}";
|
||||
|
||||
public MonitorSetup(string monitorName, string monitorInstanceId, int number, int dpi, Rect dpiAwareBounds, Rect dpiUnawareBounds)
|
||||
: base(monitorName, monitorInstanceId, number, dpi, dpiAwareBounds, dpiUnawareBounds)
|
||||
|
||||
@@ -29,10 +29,7 @@ namespace WorkspacesEditor.Models
|
||||
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
get => _name;
|
||||
|
||||
set
|
||||
{
|
||||
@@ -68,8 +65,7 @@ namespace WorkspacesEditor.Models
|
||||
|
||||
DateTime lastLaunchDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(LastLaunchedTime);
|
||||
|
||||
var now = DateTime.UtcNow.Ticks;
|
||||
var ts = DateTime.UtcNow - lastLaunchDateTime;
|
||||
TimeSpan ts = DateTime.UtcNow - lastLaunchDateTime;
|
||||
double delta = Math.Abs(ts.TotalSeconds);
|
||||
|
||||
if (delta < 1 * MINUTE)
|
||||
@@ -120,10 +116,7 @@ namespace WorkspacesEditor.Models
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanBeSaved
|
||||
{
|
||||
get => Name.Length > 0 && Applications.Count > 0;
|
||||
}
|
||||
public bool CanBeSaved => Name.Length > 0 && Applications.Count > 0;
|
||||
|
||||
private bool _isRevertEnabled;
|
||||
|
||||
@@ -145,10 +138,7 @@ namespace WorkspacesEditor.Models
|
||||
[JsonIgnore]
|
||||
public bool IsPopupVisible
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isPopupVisible;
|
||||
}
|
||||
get => _isPopupVisible;
|
||||
|
||||
set
|
||||
{
|
||||
@@ -163,11 +153,11 @@ namespace WorkspacesEditor.Models
|
||||
{
|
||||
get
|
||||
{
|
||||
List<object> applicationsListed = new List<object>();
|
||||
List<object> applicationsListed = [];
|
||||
ILookup<MonitorSetup, Application> apps = Applications.Where(x => !x.Minimized).ToLookup(x => x.MonitorSetup);
|
||||
foreach (var appItem in apps.OrderBy(x => x.Key.MonitorDpiUnawareBounds.Left).ThenBy(x => x.Key.MonitorDpiUnawareBounds.Top))
|
||||
foreach (IGrouping<MonitorSetup, Application> appItem in apps.OrderBy(x => x.Key.MonitorDpiUnawareBounds.Left).ThenBy(x => x.Key.MonitorDpiUnawareBounds.Top))
|
||||
{
|
||||
MonitorHeaderRow headerRow = new MonitorHeaderRow { MonitorName = "Screen " + appItem.Key.MonitorNumber, SelectString = Properties.Resources.SelectAllAppsOnMonitor + " " + appItem.Key.MonitorInfo };
|
||||
MonitorHeaderRow headerRow = new() { MonitorName = "Screen " + appItem.Key.MonitorNumber, SelectString = Properties.Resources.SelectAllAppsOnMonitor + " " + appItem.Key.MonitorInfo };
|
||||
applicationsListed.Add(headerRow);
|
||||
foreach (Application app in appItem)
|
||||
{
|
||||
@@ -175,10 +165,10 @@ namespace WorkspacesEditor.Models
|
||||
}
|
||||
}
|
||||
|
||||
var minimizedApps = Applications.Where(x => x.Minimized);
|
||||
IEnumerable<Application> minimizedApps = Applications.Where(x => x.Minimized);
|
||||
if (minimizedApps.Any())
|
||||
{
|
||||
MonitorHeaderRow headerRow = new MonitorHeaderRow { MonitorName = Properties.Resources.Minimized_Apps, SelectString = Properties.Resources.SelectAllMinimizedApps };
|
||||
MonitorHeaderRow headerRow = new() { MonitorName = Properties.Resources.Minimized_Apps, SelectString = Properties.Resources.SelectAllMinimizedApps };
|
||||
applicationsListed.Add(headerRow);
|
||||
foreach (Application app in minimizedApps)
|
||||
{
|
||||
@@ -219,17 +209,17 @@ namespace WorkspacesEditor.Models
|
||||
|
||||
int screenIndex = 1;
|
||||
|
||||
Monitors = new List<MonitorSetup>();
|
||||
foreach (var item in selectedProject.Monitors.OrderBy(x => x.MonitorDpiAwareBounds.Left).ThenBy(x => x.MonitorDpiAwareBounds.Top))
|
||||
Monitors = [];
|
||||
foreach (MonitorSetup item in selectedProject.Monitors.OrderBy(x => x.MonitorDpiAwareBounds.Left).ThenBy(x => x.MonitorDpiAwareBounds.Top))
|
||||
{
|
||||
Monitors.Add(item);
|
||||
screenIndex++;
|
||||
}
|
||||
|
||||
Applications = new List<Application>();
|
||||
foreach (var item in selectedProject.Applications)
|
||||
Applications = [];
|
||||
foreach (Application item in selectedProject.Applications)
|
||||
{
|
||||
Application newApp = new Application(item);
|
||||
Application newApp = new(item);
|
||||
newApp.Parent = this;
|
||||
newApp.InitializationFinished();
|
||||
Applications.Add(newApp);
|
||||
@@ -244,14 +234,14 @@ namespace WorkspacesEditor.Models
|
||||
LastLaunchedTime = project.LastLaunchedTime;
|
||||
IsShortcutNeeded = project.IsShortcutNeeded;
|
||||
MoveExistingWindows = project.MoveExistingWindows;
|
||||
Monitors = new List<MonitorSetup>() { };
|
||||
Applications = new List<Models.Application> { };
|
||||
Monitors = [];
|
||||
Applications = [];
|
||||
|
||||
foreach (var app in project.Applications)
|
||||
foreach (ProjectData.ApplicationWrapper app in project.Applications)
|
||||
{
|
||||
Models.Application newApp = new Models.Application()
|
||||
Models.Application newApp = new()
|
||||
{
|
||||
Id = string.IsNullOrEmpty(app.Id) ? $"{{{Guid.NewGuid().ToString()}}}" : app.Id,
|
||||
Id = string.IsNullOrEmpty(app.Id) ? $"{{{Guid.NewGuid()}}}" : app.Id,
|
||||
AppName = app.Application,
|
||||
AppPath = app.ApplicationPath,
|
||||
AppTitle = app.Title,
|
||||
@@ -278,20 +268,17 @@ namespace WorkspacesEditor.Models
|
||||
Applications.Add(newApp);
|
||||
}
|
||||
|
||||
foreach (var monitor in project.MonitorConfiguration)
|
||||
foreach (ProjectData.MonitorConfigurationWrapper monitor in project.MonitorConfiguration)
|
||||
{
|
||||
System.Windows.Rect dpiAware = new System.Windows.Rect(monitor.MonitorRectDpiAware.Left, monitor.MonitorRectDpiAware.Top, monitor.MonitorRectDpiAware.Width, monitor.MonitorRectDpiAware.Height);
|
||||
System.Windows.Rect dpiUnaware = new System.Windows.Rect(monitor.MonitorRectDpiUnaware.Left, monitor.MonitorRectDpiUnaware.Top, monitor.MonitorRectDpiUnaware.Width, monitor.MonitorRectDpiUnaware.Height);
|
||||
System.Windows.Rect dpiAware = new(monitor.MonitorRectDpiAware.Left, monitor.MonitorRectDpiAware.Top, monitor.MonitorRectDpiAware.Width, monitor.MonitorRectDpiAware.Height);
|
||||
System.Windows.Rect dpiUnaware = new(monitor.MonitorRectDpiUnaware.Left, monitor.MonitorRectDpiUnaware.Top, monitor.MonitorRectDpiUnaware.Width, monitor.MonitorRectDpiUnaware.Height);
|
||||
Monitors.Add(new MonitorSetup(monitor.Id, monitor.InstanceId, monitor.MonitorNumber, monitor.Dpi, dpiAware, dpiUnaware));
|
||||
}
|
||||
}
|
||||
|
||||
public BitmapImage PreviewIcons
|
||||
{
|
||||
get
|
||||
{
|
||||
return _previewIcons;
|
||||
}
|
||||
get => _previewIcons;
|
||||
|
||||
set
|
||||
{
|
||||
@@ -302,10 +289,7 @@ namespace WorkspacesEditor.Models
|
||||
|
||||
public BitmapImage PreviewImage
|
||||
{
|
||||
get
|
||||
{
|
||||
return _previewImage;
|
||||
}
|
||||
get => _previewImage;
|
||||
|
||||
set
|
||||
{
|
||||
@@ -316,10 +300,7 @@ namespace WorkspacesEditor.Models
|
||||
|
||||
public double PreviewImageWidth
|
||||
{
|
||||
get
|
||||
{
|
||||
return _previewImageWidth;
|
||||
}
|
||||
get => _previewImageWidth;
|
||||
|
||||
set
|
||||
{
|
||||
@@ -366,6 +347,7 @@ namespace WorkspacesEditor.Models
|
||||
Id = other.Id;
|
||||
Name = other.Name;
|
||||
IsRevertEnabled = true;
|
||||
MoveExistingWindows = other.MoveExistingWindows;
|
||||
}
|
||||
|
||||
internal void CloseExpanders()
|
||||
@@ -378,13 +360,13 @@ namespace WorkspacesEditor.Models
|
||||
|
||||
internal MonitorSetup GetMonitorForApp(Application app)
|
||||
{
|
||||
var monitorSetup = Monitors.Where(x => x.MonitorNumber == app.MonitorNumber).FirstOrDefault();
|
||||
MonitorSetup monitorSetup = Monitors.Where(x => x.MonitorNumber == app.MonitorNumber).FirstOrDefault();
|
||||
if (monitorSetup == null)
|
||||
{
|
||||
// monitors changed: try to determine monitor id based on middle point
|
||||
int middleX = app.Position.X + (app.Position.Width / 2);
|
||||
int middleY = app.Position.Y + (app.Position.Height / 2);
|
||||
var monitorCandidate = Monitors.Where(x =>
|
||||
MonitorSetup monitorCandidate = Monitors.Where(x =>
|
||||
(x.MonitorDpiUnawareBounds.Left < middleX) &&
|
||||
(x.MonitorDpiUnawareBounds.Right > middleX) &&
|
||||
(x.MonitorDpiUnawareBounds.Top < middleY) &&
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace WorkspacesEditor.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Add Back.
|
||||
/// Looks up a localized string similar to Add back.
|
||||
/// </summary>
|
||||
public static string AddBack {
|
||||
get {
|
||||
@@ -78,15 +78,6 @@ namespace WorkspacesEditor.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Launch new app instances.
|
||||
/// </summary>
|
||||
public static string AlwaysLaunch {
|
||||
get {
|
||||
return ResourceManager.GetString("AlwaysLaunch", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to app.
|
||||
/// </summary>
|
||||
@@ -169,7 +160,7 @@ namespace WorkspacesEditor.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Create Desktop Shortcut.
|
||||
/// Looks up a localized string similar to Create desktop shortcut.
|
||||
/// </summary>
|
||||
public static string CreateShortcut {
|
||||
get {
|
||||
@@ -186,6 +177,15 @@ namespace WorkspacesEditor.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Custom.
|
||||
/// </summary>
|
||||
public static string Custom {
|
||||
get {
|
||||
return ResourceManager.GetString("Custom", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to days ago.
|
||||
/// </summary>
|
||||
@@ -223,7 +223,7 @@ namespace WorkspacesEditor.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Remove Selected Apps.
|
||||
/// Looks up a localized string similar to Remove selected apps.
|
||||
/// </summary>
|
||||
public static string DeleteSelected {
|
||||
get {
|
||||
@@ -322,7 +322,7 @@ namespace WorkspacesEditor.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Launch & Edit.
|
||||
/// Looks up a localized string similar to Launch & edit.
|
||||
/// </summary>
|
||||
public static string LaunchEdit {
|
||||
get {
|
||||
@@ -367,7 +367,7 @@ namespace WorkspacesEditor.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Minimized Apps.
|
||||
/// Looks up a localized string similar to Minimized apps.
|
||||
/// </summary>
|
||||
public static string Minimized_Apps {
|
||||
get {
|
||||
@@ -394,7 +394,7 @@ namespace WorkspacesEditor.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Move apps if present.
|
||||
/// Looks up a localized string similar to Move existing windows.
|
||||
/// </summary>
|
||||
public static string MoveIfExist {
|
||||
get {
|
||||
@@ -502,7 +502,7 @@ namespace WorkspacesEditor.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Pin Workspaces to Taskbar.
|
||||
/// Looks up a localized string similar to Pin Workspaces to taskbar.
|
||||
/// </summary>
|
||||
public static string PinToTaskbar {
|
||||
get {
|
||||
@@ -565,7 +565,7 @@ namespace WorkspacesEditor.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Select All Apps on.
|
||||
/// Looks up a localized string similar to Select all apps on.
|
||||
/// </summary>
|
||||
public static string SelectAllAppsOnMonitor {
|
||||
get {
|
||||
@@ -574,7 +574,7 @@ namespace WorkspacesEditor.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Select All Minimized Apps.
|
||||
/// Looks up a localized string similar to Select all minimized apps.
|
||||
/// </summary>
|
||||
public static string SelectAllMinimizedApps {
|
||||
get {
|
||||
@@ -583,7 +583,7 @@ namespace WorkspacesEditor.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Select All Apps in Workspace.
|
||||
/// Looks up a localized string similar to Select all apps in Workspace.
|
||||
/// </summary>
|
||||
public static string SelectedAllInWorkspace {
|
||||
get {
|
||||
@@ -645,6 +645,15 @@ namespace WorkspacesEditor.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Window position.
|
||||
/// </summary>
|
||||
public static string WindowPosition {
|
||||
get {
|
||||
return ResourceManager.GetString("WindowPosition", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Workspace name.
|
||||
/// </summary>
|
||||
|
||||
@@ -123,9 +123,6 @@
|
||||
<data name="Admin" xml:space="preserve">
|
||||
<value>Admin</value>
|
||||
</data>
|
||||
<data name="AlwaysLaunch" xml:space="preserve">
|
||||
<value>Launch new app instances</value>
|
||||
</data>
|
||||
<data name="App" xml:space="preserve">
|
||||
<value>app</value>
|
||||
</data>
|
||||
@@ -160,6 +157,9 @@
|
||||
<data name="CreateShortcut" xml:space="preserve">
|
||||
<value>Create desktop shortcut</value>
|
||||
</data>
|
||||
<data name="Custom" xml:space="preserve">
|
||||
<value>Custom</value>
|
||||
</data>
|
||||
<data name="DaysAgo" xml:space="preserve">
|
||||
<value>days ago</value>
|
||||
</data>
|
||||
@@ -231,7 +231,7 @@
|
||||
<value>months ago</value>
|
||||
</data>
|
||||
<data name="MoveIfExist" xml:space="preserve">
|
||||
<value>Move apps if present</value>
|
||||
<value>Move existing windows</value>
|
||||
</data>
|
||||
<data name="Name" xml:space="preserve">
|
||||
<value>Name</value>
|
||||
@@ -321,6 +321,9 @@
|
||||
<data name="Width" xml:space="preserve">
|
||||
<value>Width</value>
|
||||
</data>
|
||||
<data name="WindowPosition" xml:space="preserve">
|
||||
<value>Window position</value>
|
||||
</data>
|
||||
<data name="WriteArgs" xml:space="preserve">
|
||||
<value>Write arguments here</value>
|
||||
</data>
|
||||
|
||||
@@ -20,21 +20,21 @@ namespace WorkspacesEditor.Utils
|
||||
{
|
||||
public class DrawHelper
|
||||
{
|
||||
private static Font font = new("Tahoma", 24);
|
||||
private static double scale = 0.1;
|
||||
private static readonly Font Font = new("Tahoma", 24);
|
||||
private static readonly double Scale = 0.1;
|
||||
private static double gapWidth;
|
||||
private static double gapHeight;
|
||||
|
||||
public static BitmapImage DrawPreview(Project project, Rectangle bounds, Theme currentTheme)
|
||||
{
|
||||
List<double> horizontalGaps = new List<double>();
|
||||
List<double> verticalGaps = new List<double>();
|
||||
List<double> horizontalGaps = [];
|
||||
List<double> verticalGaps = [];
|
||||
gapWidth = bounds.Width * 0.01;
|
||||
gapHeight = bounds.Height * 0.01;
|
||||
|
||||
int Scaled(double value)
|
||||
{
|
||||
return (int)(value * scale);
|
||||
return (int)(value * Scale);
|
||||
}
|
||||
|
||||
int TransformX(double posX)
|
||||
@@ -54,7 +54,7 @@ namespace WorkspacesEditor.Utils
|
||||
if (app.Maximized)
|
||||
{
|
||||
Project project = app.Parent;
|
||||
var monitor = project.GetMonitorForApp(app);
|
||||
MonitorSetup monitor = project.GetMonitorForApp(app);
|
||||
if (monitor == null)
|
||||
{
|
||||
// unrealistic case, there are no monitors at all in the workspace, use original rect
|
||||
@@ -69,22 +69,23 @@ namespace WorkspacesEditor.Utils
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary<string, int> repeatCounter = new Dictionary<string, int>();
|
||||
Dictionary<string, int> repeatCounter = [];
|
||||
|
||||
var appsIncluded = project.Applications.Where(x => x.IsIncluded);
|
||||
IEnumerable<Application> appsIncluded = project.Applications.Where(x => x.IsIncluded);
|
||||
|
||||
foreach (Application app in appsIncluded)
|
||||
{
|
||||
if (repeatCounter.TryGetValue(app.AppPath + app.AppTitle, out int value))
|
||||
string appIdentifier = app.AppPath + app.PwaAppId;
|
||||
if (repeatCounter.TryGetValue(appIdentifier, out int value))
|
||||
{
|
||||
repeatCounter[app.AppPath + app.AppTitle] = ++value;
|
||||
repeatCounter[appIdentifier] = ++value;
|
||||
}
|
||||
else
|
||||
{
|
||||
repeatCounter.Add(app.AppPath + app.AppTitle, 1);
|
||||
repeatCounter.Add(appIdentifier, 1);
|
||||
}
|
||||
|
||||
app.RepeatIndex = repeatCounter[app.AppPath + app.AppTitle];
|
||||
app.RepeatIndex = repeatCounter[appIdentifier];
|
||||
}
|
||||
|
||||
foreach (Application app in project.Applications.Where(x => !x.IsIncluded))
|
||||
@@ -113,7 +114,7 @@ namespace WorkspacesEditor.Utils
|
||||
}
|
||||
}
|
||||
|
||||
Bitmap previewBitmap = new Bitmap(Scaled(bounds.Width + (verticalGaps.Count * gapWidth)), Scaled((bounds.Height * 1.2) + (horizontalGaps.Count * gapHeight)));
|
||||
Bitmap previewBitmap = new(Scaled(bounds.Width + (verticalGaps.Count * gapWidth)), Scaled((bounds.Height * 1.2) + (horizontalGaps.Count * gapHeight)));
|
||||
double desiredIconSize = Scaled(Math.Min(bounds.Width, bounds.Height)) * 0.25;
|
||||
using (Graphics g = Graphics.FromImage(previewBitmap))
|
||||
{
|
||||
@@ -131,7 +132,7 @@ namespace WorkspacesEditor.Utils
|
||||
g.FillRectangle(monitorBrush, new Rectangle(TransformX(monitor.MonitorDpiAwareBounds.Left), TransformY(monitor.MonitorDpiAwareBounds.Top), Scaled(monitor.MonitorDpiAwareBounds.Width), Scaled(monitor.MonitorDpiAwareBounds.Height)));
|
||||
}
|
||||
|
||||
var appsToDraw = appsIncluded.Where(x => !x.Minimized);
|
||||
IEnumerable<Application> appsToDraw = appsIncluded.Where(x => !x.Minimized);
|
||||
|
||||
// draw the highlighted app at the end to have its icon in the foreground for the case there are overlapping icons
|
||||
foreach (Application app in appsToDraw.Where(x => !x.IsHighlighted))
|
||||
@@ -147,24 +148,22 @@ namespace WorkspacesEditor.Utils
|
||||
}
|
||||
|
||||
// draw the minimized windows
|
||||
Rectangle rectMinimized = new Rectangle(0, Scaled((bounds.Height * 1.02) + (horizontalGaps.Count * gapHeight)), Scaled(bounds.Width + (verticalGaps.Count * gapWidth)), Scaled(bounds.Height * 0.18));
|
||||
Rectangle rectMinimized = new(0, Scaled((bounds.Height * 1.02) + (horizontalGaps.Count * gapHeight)), Scaled(bounds.Width + (verticalGaps.Count * gapWidth)), Scaled(bounds.Height * 0.18));
|
||||
DrawWindow(g, brush, brushForHighlight, rectMinimized, appsIncluded.Where(x => x.Minimized), currentTheme);
|
||||
}
|
||||
|
||||
using (var memory = new MemoryStream())
|
||||
{
|
||||
previewBitmap.Save(memory, ImageFormat.Png);
|
||||
memory.Position = 0;
|
||||
using MemoryStream memory = new();
|
||||
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();
|
||||
BitmapImage bitmapImage = new();
|
||||
bitmapImage.BeginInit();
|
||||
bitmapImage.StreamSource = memory;
|
||||
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
|
||||
bitmapImage.EndInit();
|
||||
bitmapImage.Freeze();
|
||||
|
||||
return bitmapImage;
|
||||
}
|
||||
return bitmapImage;
|
||||
}
|
||||
|
||||
public static void DrawWindow(Graphics graphics, Brush brush, Rectangle bounds, Application app, double desiredIconSize, Theme currentTheme)
|
||||
@@ -194,7 +193,7 @@ namespace WorkspacesEditor.Utils
|
||||
}
|
||||
|
||||
double iconSize = Math.Min(Math.Min(bounds.Width - 4, bounds.Height - 4), desiredIconSize);
|
||||
Rectangle iconBounds = new Rectangle((int)(bounds.Left + (bounds.Width / 2) - (iconSize / 2)), (int)(bounds.Top + (bounds.Height / 2) - (iconSize / 2)), (int)iconSize, (int)iconSize);
|
||||
Rectangle iconBounds = new((int)(bounds.Left + (bounds.Width / 2) - (iconSize / 2)), (int)(bounds.Top + (bounds.Height / 2) - (iconSize / 2)), (int)iconSize, (int)iconSize);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -203,13 +202,13 @@ namespace WorkspacesEditor.Utils
|
||||
{
|
||||
string indexString = app.RepeatIndex.ToString(CultureInfo.InvariantCulture);
|
||||
int indexSize = (int)(iconBounds.Width * 0.5);
|
||||
Rectangle indexBounds = new Rectangle(iconBounds.Right - indexSize, iconBounds.Bottom - indexSize, indexSize, indexSize);
|
||||
Rectangle indexBounds = new(iconBounds.Right - indexSize, iconBounds.Bottom - indexSize, indexSize, indexSize);
|
||||
|
||||
var textSize = graphics.MeasureString(indexString, font);
|
||||
var state = graphics.Save();
|
||||
SizeF textSize = graphics.MeasureString(indexString, Font);
|
||||
GraphicsState state = graphics.Save();
|
||||
graphics.TranslateTransform(indexBounds.Left, indexBounds.Top);
|
||||
graphics.ScaleTransform(indexBounds.Width / textSize.Width, indexBounds.Height / textSize.Height);
|
||||
graphics.DrawString(indexString, font, Brushes.Black, PointF.Empty);
|
||||
graphics.DrawString(indexString, Font, Brushes.Black, PointF.Empty);
|
||||
graphics.Restore(state);
|
||||
}
|
||||
}
|
||||
@@ -255,7 +254,7 @@ namespace WorkspacesEditor.Utils
|
||||
for (int iconCounter = 0; iconCounter < appsCount; iconCounter++)
|
||||
{
|
||||
Application app = apps.ElementAt(iconCounter);
|
||||
Rectangle iconBounds = new Rectangle((int)(bounds.Left + (bounds.Width / 2) - (iconSize * ((appsCount / 2) - iconCounter))), (int)(bounds.Top + (bounds.Height / 2) - (iconSize / 2)), (int)iconSize, (int)iconSize);
|
||||
Rectangle iconBounds = new((int)(bounds.Left + (bounds.Width / 2) - (iconSize * ((appsCount / 2) - iconCounter))), (int)(bounds.Top + (bounds.Height / 2) - (iconSize / 2)), (int)iconSize, (int)iconSize);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -264,13 +263,13 @@ namespace WorkspacesEditor.Utils
|
||||
{
|
||||
string indexString = app.RepeatIndex.ToString(CultureInfo.InvariantCulture);
|
||||
int indexSize = (int)(iconBounds.Width * 0.5);
|
||||
Rectangle indexBounds = new Rectangle(iconBounds.Right - indexSize, iconBounds.Bottom - indexSize, indexSize, indexSize);
|
||||
Rectangle indexBounds = new(iconBounds.Right - indexSize, iconBounds.Bottom - indexSize, indexSize, indexSize);
|
||||
|
||||
var textSize = graphics.MeasureString(indexString, font);
|
||||
var state = graphics.Save();
|
||||
SizeF textSize = graphics.MeasureString(indexString, Font);
|
||||
GraphicsState state = graphics.Save();
|
||||
graphics.TranslateTransform(indexBounds.Left, indexBounds.Top);
|
||||
graphics.ScaleTransform(indexBounds.Width / textSize.Width, indexBounds.Height / textSize.Height);
|
||||
graphics.DrawString(indexString, font, Brushes.Black, PointF.Empty);
|
||||
graphics.DrawString(indexString, Font, Brushes.Black, PointF.Empty);
|
||||
graphics.Restore(state);
|
||||
}
|
||||
}
|
||||
@@ -289,14 +288,14 @@ namespace WorkspacesEditor.Utils
|
||||
return null;
|
||||
}
|
||||
|
||||
Bitmap previewBitmap = new Bitmap(32 * appsCount, 24);
|
||||
Bitmap previewBitmap = new(32 * appsCount, 24);
|
||||
using (Graphics graphics = Graphics.FromImage(previewBitmap))
|
||||
{
|
||||
graphics.SmoothingMode = SmoothingMode.AntiAlias;
|
||||
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
int appIndex = 0;
|
||||
foreach (var app in project.Applications)
|
||||
foreach (Application app in project.Applications)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -311,20 +310,18 @@ namespace WorkspacesEditor.Utils
|
||||
}
|
||||
}
|
||||
|
||||
using (var memory = new MemoryStream())
|
||||
{
|
||||
previewBitmap.Save(memory, ImageFormat.Png);
|
||||
memory.Position = 0;
|
||||
using MemoryStream memory = new();
|
||||
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();
|
||||
BitmapImage bitmapImage = new();
|
||||
bitmapImage.BeginInit();
|
||||
bitmapImage.StreamSource = memory;
|
||||
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
|
||||
bitmapImage.EndInit();
|
||||
bitmapImage.Freeze();
|
||||
|
||||
return bitmapImage;
|
||||
}
|
||||
return bitmapImage;
|
||||
}
|
||||
|
||||
private static GraphicsPath RoundedRect(Rectangle bounds)
|
||||
@@ -333,9 +330,9 @@ namespace WorkspacesEditor.Utils
|
||||
int radius = (int)(minorSize / 8);
|
||||
|
||||
int diameter = radius * 2;
|
||||
Size size = new Size(diameter, diameter);
|
||||
Rectangle arc = new Rectangle(bounds.Location, size);
|
||||
GraphicsPath path = new GraphicsPath();
|
||||
Size size = new(diameter, diameter);
|
||||
Rectangle arc = new(bounds.Location, size);
|
||||
GraphicsPath path = new();
|
||||
|
||||
if (radius == 0)
|
||||
{
|
||||
|
||||
@@ -26,18 +26,16 @@ namespace WorkspacesEditor.Utils
|
||||
{
|
||||
if (_fileSystem.File.Exists(fileName))
|
||||
{
|
||||
var attempts = 0;
|
||||
int attempts = 0;
|
||||
while (attempts < 10)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (FileSystemStream inputStream = _fileSystem.File.Open(fileName, FileMode.Open))
|
||||
using (StreamReader reader = new StreamReader(inputStream))
|
||||
{
|
||||
string data = reader.ReadToEnd();
|
||||
inputStream.Close();
|
||||
return data;
|
||||
}
|
||||
using FileSystemStream inputStream = _fileSystem.File.Open(fileName, FileMode.Open);
|
||||
using StreamReader reader = new(inputStream);
|
||||
string data = reader.ReadToEnd();
|
||||
inputStream.Close();
|
||||
return data;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace WorkspacesEditor.Utils
|
||||
|
||||
private Screen[] GetDpiUnawareScreenBounds()
|
||||
{
|
||||
Thread dpiUnawareThread = new Thread(new ThreadStart(SaveDpiUnawareScreens));
|
||||
Thread dpiUnawareThread = new(new ThreadStart(SaveDpiUnawareScreens));
|
||||
dpiUnawareThread.Start();
|
||||
dpiUnawareThread.Join();
|
||||
|
||||
@@ -35,15 +35,15 @@ namespace WorkspacesEditor.Utils
|
||||
|
||||
public static Screen[] GetDpiUnawareScreens()
|
||||
{
|
||||
MonitorHelper monitorHelper = new MonitorHelper();
|
||||
MonitorHelper monitorHelper = new();
|
||||
return monitorHelper.GetDpiUnawareScreenBounds();
|
||||
}
|
||||
|
||||
internal static double GetScreenDpiFromScreen(Screen screen)
|
||||
{
|
||||
var point = new System.Drawing.Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1);
|
||||
var monitor = NativeMethods.MonitorFromPoint(point, NativeMethods._MONITOR_DEFAULTTONEAREST);
|
||||
NativeMethods.GetDpiForMonitor(monitor, NativeMethods.DpiType.EFFECTIVE, out uint dpiX, out uint dpiY);
|
||||
System.Drawing.Point point = new(screen.Bounds.Left + 1, screen.Bounds.Top + 1);
|
||||
nint monitor = NativeMethods.MonitorFromPoint(point, NativeMethods._MONITOR_DEFAULTTONEAREST);
|
||||
_ = NativeMethods.GetDpiForMonitor(monitor, NativeMethods.DpiType.EFFECTIVE, out uint dpiX, out _);
|
||||
return dpiX / 96.0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,19 +4,12 @@
|
||||
|
||||
namespace WorkspacesEditor.Utils
|
||||
{
|
||||
public struct ParsingResult
|
||||
public readonly struct ParsingResult(bool result, string message = "", string data = "")
|
||||
{
|
||||
public bool Result { get; }
|
||||
public bool Result { get; } = result;
|
||||
|
||||
public string Message { get; }
|
||||
public string Message { get; } = message;
|
||||
|
||||
public string MalformedData { get; }
|
||||
|
||||
public ParsingResult(bool result, string message = "", string data = "")
|
||||
{
|
||||
Result = result;
|
||||
Message = message;
|
||||
MalformedData = data;
|
||||
}
|
||||
public string MalformedData { get; } = data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,13 +9,13 @@ namespace WorkspacesEditor.Utils
|
||||
public class Settings
|
||||
{
|
||||
private const string WorkspacesModuleName = "Workspaces";
|
||||
private static SettingsUtils _settingsUtils = new SettingsUtils();
|
||||
private static readonly SettingsUtils _settingsUtils = new();
|
||||
|
||||
public static WorkspacesSettings ReadSettings()
|
||||
{
|
||||
if (!_settingsUtils.SettingsExists(WorkspacesModuleName))
|
||||
{
|
||||
var defaultWorkspacesSettings = new WorkspacesSettings();
|
||||
WorkspacesSettings defaultWorkspacesSettings = new();
|
||||
defaultWorkspacesSettings.Save(_settingsUtils);
|
||||
return defaultWorkspacesSettings;
|
||||
}
|
||||
|
||||
@@ -11,12 +11,9 @@ namespace WorkspacesEditor.Utils
|
||||
public static string UpperCamelCaseToDashCase(this string str)
|
||||
{
|
||||
// If it's single letter variable, leave it as it is
|
||||
if (str.Length == 1)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
|
||||
return string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "-" + x.ToString() : x.ToString())).ToLowerInvariant();
|
||||
return str.Length == 1
|
||||
? str
|
||||
: string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "-" + x.ToString() : x.ToString())).ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace WorkspacesEditor.Utils
|
||||
{
|
||||
try
|
||||
{
|
||||
WorkspacesData parser = new WorkspacesData();
|
||||
WorkspacesData parser = new();
|
||||
if (!File.Exists(parser.File))
|
||||
{
|
||||
Logger.LogWarning($"Workspaces storage file not found: {parser.File}");
|
||||
@@ -56,14 +56,14 @@ namespace WorkspacesEditor.Utils
|
||||
{
|
||||
try
|
||||
{
|
||||
ProjectData parser = new ProjectData();
|
||||
ProjectData parser = new();
|
||||
if (!File.Exists(TempProjectData.File))
|
||||
{
|
||||
Logger.LogWarning($"ParseProject method. Workspaces storage file not found: {TempProjectData.File}");
|
||||
return null;
|
||||
}
|
||||
|
||||
Project project = new Project(parser.Read(TempProjectData.File));
|
||||
Project project = new(parser.Read(TempProjectData.File));
|
||||
return project;
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -75,13 +75,13 @@ namespace WorkspacesEditor.Utils
|
||||
|
||||
public void SerializeWorkspaces(List<Project> workspaces, bool useTempFile = false)
|
||||
{
|
||||
WorkspacesData serializer = new WorkspacesData();
|
||||
WorkspacesData.WorkspacesListWrapper workspacesWrapper = new WorkspacesData.WorkspacesListWrapper { };
|
||||
workspacesWrapper.Workspaces = new List<ProjectData.ProjectWrapper>();
|
||||
WorkspacesData serializer = new();
|
||||
WorkspacesData.WorkspacesListWrapper workspacesWrapper = new() { };
|
||||
workspacesWrapper.Workspaces = [];
|
||||
|
||||
foreach (Project project in workspaces)
|
||||
{
|
||||
ProjectData.ProjectWrapper wrapper = new ProjectData.ProjectWrapper
|
||||
ProjectData.ProjectWrapper wrapper = new()
|
||||
{
|
||||
Id = project.Id,
|
||||
Name = project.Name,
|
||||
@@ -89,11 +89,11 @@ namespace WorkspacesEditor.Utils
|
||||
IsShortcutNeeded = project.IsShortcutNeeded,
|
||||
MoveExistingWindows = project.MoveExistingWindows,
|
||||
LastLaunchedTime = project.LastLaunchedTime,
|
||||
Applications = new List<ProjectData.ApplicationWrapper> { },
|
||||
MonitorConfiguration = new List<ProjectData.MonitorConfigurationWrapper> { },
|
||||
Applications = [],
|
||||
MonitorConfiguration = [],
|
||||
};
|
||||
|
||||
foreach (var app in project.Applications.Where(x => x.IsIncluded))
|
||||
foreach (Application app in project.Applications.Where(x => x.IsIncluded))
|
||||
{
|
||||
wrapper.Applications.Add(new ProjectData.ApplicationWrapper
|
||||
{
|
||||
@@ -120,7 +120,7 @@ namespace WorkspacesEditor.Utils
|
||||
});
|
||||
}
|
||||
|
||||
foreach (var monitor in project.Monitors)
|
||||
foreach (MonitorSetup monitor in project.Monitors)
|
||||
{
|
||||
wrapper.MonitorConfiguration.Add(new ProjectData.MonitorConfigurationWrapper
|
||||
{
|
||||
@@ -150,7 +150,7 @@ namespace WorkspacesEditor.Utils
|
||||
|
||||
try
|
||||
{
|
||||
IOUtils ioUtils = new IOUtils();
|
||||
IOUtils ioUtils = new();
|
||||
ioUtils.WriteFile(useTempFile ? TempProjectData.File : serializer.File, serializer.Serialize(workspacesWrapper));
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -162,7 +162,7 @@ namespace WorkspacesEditor.Utils
|
||||
|
||||
private bool AddWorkspaces(MainViewModel mainViewModel, WorkspacesData.WorkspacesListWrapper workspaces)
|
||||
{
|
||||
foreach (var project in workspaces.Workspaces)
|
||||
foreach (ProjectData.ProjectWrapper project in workspaces.Workspaces)
|
||||
{
|
||||
mainViewModel.Workspaces.Add(new Project(project));
|
||||
}
|
||||
@@ -173,13 +173,13 @@ namespace WorkspacesEditor.Utils
|
||||
|
||||
private bool SetWorkspaces(MainViewModel mainViewModel, WorkspacesData.WorkspacesListWrapper workspaces)
|
||||
{
|
||||
mainViewModel.Workspaces = new System.Collections.ObjectModel.ObservableCollection<Project> { };
|
||||
mainViewModel.Workspaces = [];
|
||||
return AddWorkspaces(mainViewModel, workspaces);
|
||||
}
|
||||
|
||||
internal void SerializeTempProject(Project project)
|
||||
{
|
||||
SerializeWorkspaces(new List<Project>() { project }, true);
|
||||
SerializeWorkspaces([project], true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,24 +156,33 @@
|
||||
Content="{x:Static props:Resources.LaunchAsAdmin}"
|
||||
IsChecked="{Binding IsElevated, Mode=TwoWay}"
|
||||
IsEnabled="{Binding CanLaunchElevated, Mode=OneWay}" />
|
||||
<CheckBox
|
||||
MinWidth="10"
|
||||
Margin="15,0,0,0"
|
||||
Checked="MaximizedChecked"
|
||||
Content="{x:Static props:Resources.Maximized}"
|
||||
IsChecked="{Binding Maximized, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<CheckBox
|
||||
MinWidth="10"
|
||||
Margin="15,0,0,0"
|
||||
Checked="MinimizedChecked"
|
||||
Content="{x:Static props:Resources.Minimized}"
|
||||
IsChecked="{Binding Minimized, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Row="2"
|
||||
Margin="100,5,0,0"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
FontWeight="Normal"
|
||||
IsEnabled="{Binding EditPositionEnabled, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
Style="{StaticResource TextBlockEnabledStyle}"
|
||||
Text="{x:Static props:Resources.WindowPosition}" />
|
||||
<ComboBox
|
||||
Margin="15,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Background="{DynamicResource SecondaryBackgroundBrush}"
|
||||
BorderBrush="{DynamicResource PrimaryBorderBrush}"
|
||||
BorderThickness="2"
|
||||
FontSize="14"
|
||||
FontWeight="Normal"
|
||||
SelectedIndex="{Binding PositionComboboxIndex, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
|
||||
<ComboBoxItem Content="{x:Static props:Resources.Custom}" />
|
||||
<ComboBoxItem Content="{x:Static props:Resources.Maximized}" />
|
||||
<ComboBoxItem Content="{x:Static props:Resources.Minimized}" />
|
||||
</ComboBox>
|
||||
<TextBlock
|
||||
Margin="15,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
FontWeight="Normal"
|
||||
@@ -267,14 +276,12 @@
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal">
|
||||
<Button
|
||||
Margin="0,20,0,20"
|
||||
Margin="0,10,0,20"
|
||||
VerticalAlignment="Center"
|
||||
Background="Transparent"
|
||||
Click="CancelButtonClicked">
|
||||
@@ -300,27 +307,8 @@
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
Text="{Binding EditorWindowTitle}" />
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="1" Orientation="Vertical">
|
||||
<TextBlock
|
||||
FontSize="14"
|
||||
FontWeight="Normal"
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
Text="{x:Static props:Resources.WorkspaceName}" />
|
||||
<TextBox
|
||||
x:Name="EditNameTextBox"
|
||||
Width="320"
|
||||
Margin="0,6,0,6"
|
||||
HorizontalAlignment="Left"
|
||||
Background="{DynamicResource SecondaryBackgroundBrush}"
|
||||
BorderBrush="{DynamicResource PrimaryBorderBrush}"
|
||||
BorderThickness="2"
|
||||
GotFocus="EditNameTextBox_GotFocus"
|
||||
KeyDown="EditNameTextBoxKeyDown"
|
||||
Text="{Binding Name, Mode=TwoWay}"
|
||||
TextChanged="EditNameTextBox_TextChanged" />
|
||||
</StackPanel>
|
||||
<Border
|
||||
Grid.Row="2"
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="{DynamicResource MonitorViewBackgroundBrush}"
|
||||
CornerRadius="5">
|
||||
@@ -361,8 +349,47 @@
|
||||
DockPanel.Dock="Right" />
|
||||
</DockPanel>
|
||||
</Border>
|
||||
<DockPanel Grid.Row="2" HorizontalAlignment="Stretch">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock
|
||||
Margin="0,10,0,0"
|
||||
FontSize="14"
|
||||
FontWeight="Normal"
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
Text="{x:Static props:Resources.WorkspaceName}" />
|
||||
<TextBox
|
||||
x:Name="EditNameTextBox"
|
||||
Width="300"
|
||||
Margin="0,6,0,6"
|
||||
HorizontalAlignment="Left"
|
||||
Background="{DynamicResource SecondaryBackgroundBrush}"
|
||||
BorderBrush="{DynamicResource PrimaryBorderBrush}"
|
||||
BorderThickness="2"
|
||||
GotFocus="EditNameTextBox_GotFocus"
|
||||
KeyDown="EditNameTextBoxKeyDown"
|
||||
Text="{Binding Name, Mode=TwoWay}"
|
||||
TextChanged="EditNameTextBox_TextChanged" />
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
HorizontalAlignment="Right"
|
||||
DockPanel.Dock="Right"
|
||||
Orientation="Horizontal">
|
||||
<CheckBox
|
||||
Margin="20,0,0,0"
|
||||
VerticalAlignment="Bottom"
|
||||
Content="{x:Static props:Resources.CreateShortcut}"
|
||||
FontSize="14"
|
||||
IsChecked="{Binding IsShortcutNeeded, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<CheckBox
|
||||
Margin="20,0,0,0"
|
||||
VerticalAlignment="Bottom"
|
||||
Content="{x:Static props:Resources.MoveIfExist}"
|
||||
FontSize="14"
|
||||
IsChecked="{Binding MoveExistingWindows, Mode=TwoWay}" />
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
<ScrollViewer
|
||||
Grid.Row="4"
|
||||
Grid.Row="3"
|
||||
Margin="0,10,0,0"
|
||||
PreviewMouseWheel="ScrollViewer_PreviewMouseWheel"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
@@ -377,52 +404,31 @@
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
<StackPanel
|
||||
Grid.Row="5"
|
||||
Margin="0,5,0,0"
|
||||
Orientation="Horizontal"
|
||||
Visibility="Collapsed">
|
||||
<CheckBox
|
||||
Margin="0,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Content="{x:Static props:Resources.MoveIfExist}"
|
||||
FontSize="14"
|
||||
FontWeight="Normal"
|
||||
Foreground="{DynamicResource PrimaryForegroundBrush}"
|
||||
IsChecked="{Binding MoveExistingWindows, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
Grid.Row="4"
|
||||
Margin="40,20,0,0"
|
||||
HorizontalAlignment="Right"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
x:Name="CancelButton"
|
||||
Height="36"
|
||||
Margin="20,0,0,0"
|
||||
Padding="24,0,24,0"
|
||||
AutomationProperties.Name="{x:Static props:Resources.Cancel}"
|
||||
Background="{DynamicResource SecondaryBackgroundBrush}"
|
||||
BorderBrush="{DynamicResource PrimaryBorderBrush}"
|
||||
BorderThickness="2"
|
||||
Click="CancelButtonClicked"
|
||||
Content="{x:Static props:Resources.Cancel}" />
|
||||
<Button
|
||||
x:Name="SaveButton"
|
||||
Height="36"
|
||||
Margin="20,0,0,0"
|
||||
Padding="24,0,24,0"
|
||||
AutomationProperties.Name="{x:Static props:Resources.Save_Workspace}"
|
||||
Click="SaveButtonClicked"
|
||||
Content="{x:Static props:Resources.Save_Workspace}"
|
||||
IsEnabled="{Binding CanBeSaved, UpdateSourceTrigger=PropertyChanged}"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
</StackPanel>
|
||||
<DockPanel Grid.Row="6" Margin="0,20,0,20">
|
||||
<CheckBox
|
||||
Content="{x:Static props:Resources.CreateShortcut}"
|
||||
DockPanel.Dock="Left"
|
||||
FontSize="14"
|
||||
IsChecked="{Binding IsShortcutNeeded, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<StackPanel
|
||||
Margin="40,0,0,0"
|
||||
HorizontalAlignment="Right"
|
||||
DockPanel.Dock="Right"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
x:Name="CancelButton"
|
||||
Height="36"
|
||||
Margin="20,0,0,0"
|
||||
Padding="24,0,24,0"
|
||||
AutomationProperties.Name="{x:Static props:Resources.Cancel}"
|
||||
Background="{DynamicResource SecondaryBackgroundBrush}"
|
||||
BorderBrush="{DynamicResource PrimaryBorderBrush}"
|
||||
BorderThickness="2"
|
||||
Click="CancelButtonClicked"
|
||||
Content="{x:Static props:Resources.Cancel}" />
|
||||
<Button
|
||||
x:Name="SaveButton"
|
||||
Height="36"
|
||||
Margin="20,0,0,0"
|
||||
Padding="24,0,24,0"
|
||||
AutomationProperties.Name="{x:Static props:Resources.Save_Workspace}"
|
||||
Click="SaveButtonClicked"
|
||||
Content="{x:Static props:Resources.Save_Workspace}"
|
||||
IsEnabled="{Binding CanBeSaved, UpdateSourceTrigger=PropertyChanged}"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
</Page>
|
||||
|
||||
@@ -185,20 +185,6 @@ namespace WorkspacesEditor
|
||||
application.CommandLineTextChanged(textBox.Text);
|
||||
}
|
||||
|
||||
private void MaximizedChecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
CheckBox checkBox = sender as CheckBox;
|
||||
Models.Application application = checkBox.DataContext as Models.Application;
|
||||
application.MaximizedChecked();
|
||||
}
|
||||
|
||||
private void MinimizedChecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
CheckBox checkBox = sender as CheckBox;
|
||||
Models.Application application = checkBox.DataContext as Models.Application;
|
||||
application.MinimizedChecked();
|
||||
}
|
||||
|
||||
private void LaunchEditButtonClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Button button = sender as Button;
|
||||
|
||||
@@ -101,7 +101,7 @@ void Launcher::Launch() // Launching thread
|
||||
const long ms = 100;
|
||||
|
||||
// Launch apps
|
||||
for (auto appState = m_launchingStatus.GetNext(LaunchingState::Waiting); appState.has_value(); appState = m_launchingStatus.GetNext(LaunchingState::Waiting))
|
||||
for (auto appState = m_launchingStatus.GetNext(LaunchingState::Waiting);appState.has_value();appState = m_launchingStatus.GetNext(LaunchingState::Waiting))
|
||||
{
|
||||
auto app = appState.value().application;
|
||||
|
||||
@@ -157,7 +157,7 @@ void Launcher::Launch() // Launching thread
|
||||
{
|
||||
std::lock_guard lock(m_uiHelperMutex);
|
||||
m_uiHelper->UpdateLaunchStatus(m_launchingStatus.Get());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,9 @@ namespace Utils
|
||||
constexpr const wchar_t* PowerToysSettings = L"PowerToys.Settings.exe";
|
||||
constexpr const wchar_t* ApplicationFrameHost = L"APPLICATIONFRAMEHOST.EXE";
|
||||
constexpr const wchar_t* Exe = L".EXE";
|
||||
|
||||
constexpr const wchar_t* EdgeFilename = L"msedge.exe";
|
||||
constexpr const wchar_t* ChromeFilename = L"chrome.exe";
|
||||
}
|
||||
|
||||
AppList IterateAppsFolder()
|
||||
@@ -214,7 +217,7 @@ namespace Utils
|
||||
std::wstring appPathUpper(appPath);
|
||||
std::transform(appPathUpper.begin(), appPathUpper.end(), appPathUpper.begin(), towupper);
|
||||
|
||||
// filter out ApplicationFrameHost.exe
|
||||
// filter out ApplicationFrameHost.exe
|
||||
if (appPathUpper.ends_with(NonLocalizable::ApplicationFrameHost))
|
||||
{
|
||||
return std::nullopt;
|
||||
@@ -325,7 +328,7 @@ namespace Utils
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return AppData{
|
||||
.name = std::filesystem::path(appPath).stem(),
|
||||
.installPath = appPath
|
||||
@@ -335,7 +338,7 @@ namespace Utils
|
||||
std::optional<AppData> GetApp(HWND window, const AppList& apps)
|
||||
{
|
||||
std::wstring processPath = get_process_path(window);
|
||||
|
||||
|
||||
DWORD pid{};
|
||||
GetWindowThreadProcessId(window, &pid);
|
||||
|
||||
@@ -378,5 +381,15 @@ namespace Utils
|
||||
return updated;
|
||||
}
|
||||
|
||||
bool AppData::IsEdge() const
|
||||
{
|
||||
return installPath.ends_with(NonLocalizable::EdgeFilename);
|
||||
}
|
||||
|
||||
bool AppData::IsChrome() const
|
||||
{
|
||||
return installPath.ends_with(NonLocalizable::ChromeFilename);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,11 @@ namespace Utils
|
||||
std::wstring installPath;
|
||||
std::wstring packageFullName;
|
||||
std::wstring appUserModelId;
|
||||
std::wstring pwaAppId;
|
||||
bool canLaunchElevated = false;
|
||||
|
||||
bool IsEdge() const;
|
||||
bool IsChrome() const;
|
||||
};
|
||||
|
||||
using AppList = std::vector<AppData>;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "pch.h"
|
||||
#include "PwaHelper.h"
|
||||
#include "AppUtils.h"
|
||||
#include <ShlObj.h>
|
||||
#include <tlhelp32.h>
|
||||
#include <winternl.h>
|
||||
@@ -11,7 +12,7 @@
|
||||
#include <wil\com.h>
|
||||
#pragma comment(lib, "ntdll.lib")
|
||||
|
||||
namespace SnapshotUtils
|
||||
namespace Utils
|
||||
{
|
||||
namespace NonLocalizable
|
||||
{
|
||||
@@ -22,6 +23,7 @@ namespace SnapshotUtils
|
||||
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);
|
||||
|
||||
@@ -48,9 +50,10 @@ namespace SnapshotUtils
|
||||
virtual HRESULT STDMETHODCALLTYPE GetAppIDForProcess(DWORD dwProcessId, WCHAR** pszAppId, void* pUnknown1, void* pUnknown2, void* pUnknown3) = 0;
|
||||
};
|
||||
|
||||
BOOL GetAppId_7(HWND hWnd, std::wstring* result)
|
||||
std::optional<std::wstring> PwaHelper::GetAppId_7(HWND hWnd) const
|
||||
{
|
||||
HRESULT hr;
|
||||
std::optional<std::wstring> result = std::nullopt;
|
||||
|
||||
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()));
|
||||
@@ -60,19 +63,19 @@ namespace SnapshotUtils
|
||||
hr = appResolver->GetAppIDForWindow(hWnd, &pszAppId, NULL, NULL, NULL);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
*result = std::wstring(pszAppId.get());
|
||||
result = std::wstring(pszAppId.get());
|
||||
}
|
||||
|
||||
appResolver->Release();
|
||||
}
|
||||
|
||||
return SUCCEEDED(hr);
|
||||
return result;
|
||||
}
|
||||
|
||||
BOOL GetAppId_8(HWND hWnd, std::wstring* result)
|
||||
std::optional<std::wstring> PwaHelper::GetAppId_8(HWND hWnd) const
|
||||
{
|
||||
HRESULT hr;
|
||||
*result = L"";
|
||||
std::optional<std::wstring> result = std::nullopt;
|
||||
|
||||
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()));
|
||||
@@ -82,29 +85,29 @@ namespace SnapshotUtils
|
||||
hr = appResolver->GetAppIDForWindow(hWnd, &pszAppId, NULL, NULL, NULL);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
*result = std::wstring(pszAppId.get());
|
||||
result = std::wstring(pszAppId.get());
|
||||
}
|
||||
|
||||
appResolver->Release();
|
||||
}
|
||||
|
||||
return SUCCEEDED(hr);
|
||||
return result;
|
||||
}
|
||||
|
||||
BOOL PwaHelper::GetAppId(HWND hWnd, std::wstring* result)
|
||||
std::wstring PwaHelper::GetAppId(HWND hWnd) const
|
||||
{
|
||||
HRESULT hr = GetAppId_8(hWnd, result);
|
||||
if (!SUCCEEDED(hr))
|
||||
std::optional<std::wstring> result = GetAppId_8(hWnd);
|
||||
if (result == std::nullopt)
|
||||
{
|
||||
hr = GetAppId_7(hWnd, result);
|
||||
result = GetAppId_7(hWnd);
|
||||
}
|
||||
return SUCCEEDED(hr);
|
||||
return result.has_value() ? result.value() : L"";
|
||||
}
|
||||
|
||||
BOOL GetProcessId_7(DWORD dwProcessId, std::wstring* result)
|
||||
std::optional<std::wstring> PwaHelper::GetProcessId_7(DWORD dwProcessId) const
|
||||
{
|
||||
HRESULT hr;
|
||||
*result = L"";
|
||||
std::optional<std::wstring> result = std::nullopt;
|
||||
|
||||
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()));
|
||||
@@ -114,19 +117,19 @@ namespace SnapshotUtils
|
||||
hr = appResolver->GetAppIDForProcess(dwProcessId, &pszAppId, NULL, NULL, NULL);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
*result = std::wstring(pszAppId.get());
|
||||
result = std::wstring(pszAppId.get());
|
||||
}
|
||||
|
||||
appResolver->Release();
|
||||
}
|
||||
|
||||
return SUCCEEDED(hr);
|
||||
return result;
|
||||
}
|
||||
|
||||
BOOL GetProcessId_8(DWORD dwProcessId, std::wstring* result)
|
||||
std::optional<std::wstring> PwaHelper::GetProcessId_8(DWORD dwProcessId) const
|
||||
{
|
||||
HRESULT hr;
|
||||
*result = L"";
|
||||
std::optional<std::wstring> result = std::nullopt;
|
||||
|
||||
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()));
|
||||
@@ -136,23 +139,23 @@ namespace SnapshotUtils
|
||||
hr = appResolver->GetAppIDForProcess(dwProcessId, &pszAppId, NULL, NULL, NULL);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
*result = std::wstring(pszAppId.get());
|
||||
result = std::wstring(pszAppId.get());
|
||||
}
|
||||
|
||||
appResolver->Release();
|
||||
}
|
||||
|
||||
return SUCCEEDED(hr);
|
||||
return result;
|
||||
}
|
||||
|
||||
BOOL GetProcessId(DWORD dwProcessId, std::wstring* result)
|
||||
std::wstring PwaHelper::GetProcessId(DWORD dwProcessId) const
|
||||
{
|
||||
HRESULT hr = GetProcessId_8(dwProcessId, result);
|
||||
if (!SUCCEEDED(hr))
|
||||
std::optional<std::wstring> result = GetProcessId_8(dwProcessId);
|
||||
if (result == std::nullopt)
|
||||
{
|
||||
hr = GetProcessId_7(dwProcessId, result);
|
||||
result = GetProcessId_7(dwProcessId);
|
||||
}
|
||||
return SUCCEEDED(hr);
|
||||
return result.has_value() ? result.value() : L"";
|
||||
}
|
||||
|
||||
std::wstring GetProcCommandLine(DWORD pid)
|
||||
@@ -244,7 +247,7 @@ namespace SnapshotUtils
|
||||
|
||||
void PwaHelper::InitAumidToAppId()
|
||||
{
|
||||
if (pwaAumidToAppId.size() > 0)
|
||||
if (m_pwaAumidToAppId.size() > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -254,7 +257,7 @@ namespace SnapshotUtils
|
||||
for (const auto subProcessID : pwaHelperProcessIds)
|
||||
{
|
||||
std::wstring aumidID;
|
||||
GetProcessId(subProcessID, &aumidID);
|
||||
aumidID = GetProcessId(subProcessID);
|
||||
std::wstring commandLineArg = GetProcCommandLine(subProcessID);
|
||||
auto appIdIndexStart = commandLineArg.find(NonLocalizable::EdgeAppIdIdentifier);
|
||||
if (appIdIndexStart != std::wstring::npos)
|
||||
@@ -267,7 +270,7 @@ namespace SnapshotUtils
|
||||
}
|
||||
}
|
||||
std::wstring appId{ commandLineArg };
|
||||
pwaAumidToAppId.insert(std::map<std::wstring, std::wstring>::value_type(aumidID, appId));
|
||||
m_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;
|
||||
@@ -293,7 +296,7 @@ namespace SnapshotUtils
|
||||
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()));
|
||||
m_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);
|
||||
}
|
||||
}
|
||||
@@ -307,41 +310,39 @@ namespace SnapshotUtils
|
||||
}
|
||||
}
|
||||
|
||||
BOOL PwaHelper::GetPwaAppId(std::wstring windowAumid, std::wstring* result)
|
||||
std::optional<std::wstring> PwaHelper::GetPwaAppId(const std::wstring& windowAumid) const
|
||||
{
|
||||
const auto pwaIndex = pwaAumidToAppId.find(windowAumid);
|
||||
if (pwaIndex != pwaAumidToAppId.end())
|
||||
const auto pwaIndex = m_pwaAumidToAppId.find(windowAumid);
|
||||
if (pwaIndex != m_pwaAumidToAppId.end())
|
||||
{
|
||||
*result = pwaIndex->second;
|
||||
return true;
|
||||
return pwaIndex->second;
|
||||
}
|
||||
|
||||
return false;
|
||||
return std::nullopt;
|
||||
;
|
||||
}
|
||||
|
||||
BOOL PwaHelper::SearchPwaName(std::wstring pwaAppId, std::wstring windowAumid, std::wstring* pwaName)
|
||||
std::wstring PwaHelper::SearchPwaName(const std::wstring& pwaAppId, const std::wstring& windowAumid) const
|
||||
{
|
||||
const auto index = pwaAppIdsToAppNames.find(pwaAppId);
|
||||
if (index != pwaAppIdsToAppNames.end())
|
||||
const auto index = m_pwaAppIdsToAppNames.find(pwaAppId);
|
||||
if (index != m_pwaAppIdsToAppNames.end())
|
||||
{
|
||||
*pwaName = index->second;
|
||||
return true;
|
||||
return index->second;
|
||||
}
|
||||
|
||||
std::wstring nameFromAumid{ windowAumid };
|
||||
const std::size_t delimiterPos = nameFromAumid.find(L"-");
|
||||
if (delimiterPos != std::string::npos)
|
||||
{
|
||||
nameFromAumid = nameFromAumid.substr(0, delimiterPos);
|
||||
return nameFromAumid.substr(0, delimiterPos);
|
||||
}
|
||||
|
||||
*pwaName = nameFromAumid;
|
||||
return false;
|
||||
return nameFromAumid;
|
||||
}
|
||||
|
||||
void PwaHelper::InitChromeAppIds()
|
||||
{
|
||||
if (chromeAppIds.size() > 0)
|
||||
if (m_chromeAppIds.size() > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -360,7 +361,7 @@ namespace SnapshotUtils
|
||||
if (directoryName.wstring().find(NonLocalizable::ChromeDirPrefix) == 0)
|
||||
{
|
||||
const std::wstring appId = directoryName.wstring().substr(NonLocalizable::ChromeDirPrefix.size());
|
||||
chromeAppIds.push_back(appId);
|
||||
m_chromeAppIds.push_back(appId);
|
||||
for (const auto& filename : std::filesystem::directory_iterator(directory))
|
||||
{
|
||||
if (!filename.is_directory())
|
||||
@@ -368,7 +369,7 @@ namespace SnapshotUtils
|
||||
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()));
|
||||
m_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);
|
||||
}
|
||||
}
|
||||
@@ -380,30 +381,73 @@ namespace SnapshotUtils
|
||||
}
|
||||
}
|
||||
|
||||
BOOL PwaHelper::SearchPwaAppId(std::wstring windowAumid, std::wstring* pwaAppId)
|
||||
std::optional<std::wstring> PwaHelper::SearchPwaAppId(const std::wstring& windowAumid) const
|
||||
{
|
||||
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" ");
|
||||
std::wstring windowAumidSub = windowAumid.substr(appIdIndexStart + NonLocalizable::ChromeAppIdIdentifier.size());
|
||||
const auto appIdIndexEnd = windowAumidSub.find(L" ");
|
||||
if (appIdIndexEnd != std::wstring::npos)
|
||||
{
|
||||
windowAumid = windowAumid.substr(0, appIdIndexEnd);
|
||||
windowAumidSub = windowAumidSub.substr(0, appIdIndexEnd);
|
||||
}
|
||||
|
||||
const std::wstring windowAumidBegin = windowAumid.substr(0, 10);
|
||||
for (const auto chromeAppId : chromeAppIds)
|
||||
const std::wstring windowAumidBegin = windowAumidSub.substr(0, 10);
|
||||
for (const auto chromeAppId : m_chromeAppIds)
|
||||
{
|
||||
if (chromeAppId.find(windowAumidBegin) == 0)
|
||||
{
|
||||
*pwaAppId = chromeAppId;
|
||||
return true;
|
||||
return chromeAppId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*pwaAppId = L"";
|
||||
return false;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void PwaHelper::UpdatePwaApp(Utils::Apps::AppData* appData, HWND window)
|
||||
{
|
||||
std::optional<std::wstring> pwaAppId = std::nullopt;
|
||||
std::wstring finalName = appData->name;
|
||||
std::wstring pwaName = L"";
|
||||
if (appData->IsEdge())
|
||||
{
|
||||
InitAumidToAppId();
|
||||
|
||||
std::wstring windowAumid = GetAppId(window);
|
||||
|
||||
Logger::info(L"Found an edge window with aumid {}", windowAumid);
|
||||
|
||||
pwaAppId = GetPwaAppId(windowAumid);
|
||||
if (pwaAppId.has_value())
|
||||
{
|
||||
Logger::info(L"The found edge window is a PWA app with appId {}", pwaAppId.value());
|
||||
pwaName = SearchPwaName(pwaAppId.value(), windowAumid);
|
||||
Logger::info(L"The found edge window is a PWA app with name {}", pwaName);
|
||||
finalName = pwaName + L" (" + finalName + L")";
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::info(L"The found edge window does not contain a PWA app");
|
||||
}
|
||||
}
|
||||
else if (appData->IsChrome())
|
||||
{
|
||||
InitChromeAppIds();
|
||||
|
||||
std::wstring windowAumid = GetAppId(window);
|
||||
Logger::info(L"Found a chrome window with aumid {}", windowAumid);
|
||||
|
||||
pwaAppId = SearchPwaAppId(windowAumid);
|
||||
if (pwaAppId.has_value())
|
||||
{
|
||||
pwaName = SearchPwaName(pwaAppId.value(), windowAumid);
|
||||
finalName = pwaName + L" (" + finalName + L")";
|
||||
}
|
||||
}
|
||||
|
||||
appData->name = finalName;
|
||||
appData->pwaAppId = pwaAppId.has_value() ? pwaAppId.value() : L"";
|
||||
}
|
||||
}
|
||||
29
src/modules/Workspaces/WorkspacesLib/PwaHelper.h
Normal file
29
src/modules/Workspaces/WorkspacesLib/PwaHelper.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <WorkspacesLib/AppUtils.h>
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
class PwaHelper
|
||||
{
|
||||
public:
|
||||
void UpdatePwaApp(Apps::AppData* appData, HWND window);
|
||||
|
||||
private:
|
||||
std::map<std::wstring, std::wstring> m_pwaAumidToAppId;
|
||||
std::vector<std::wstring> m_chromeAppIds;
|
||||
std::map<std::wstring, std::wstring> m_pwaAppIdsToAppNames;
|
||||
|
||||
void InitAumidToAppId();
|
||||
void InitChromeAppIds();
|
||||
std::optional<std::wstring> GetAppId_7(HWND hWnd) const;
|
||||
std::optional<std::wstring> GetAppId_8(HWND hWnd) const;
|
||||
std::wstring GetAppId(HWND hWnd) const;
|
||||
std::optional<std::wstring> GetProcessId_7(DWORD dwProcessId) const;
|
||||
std::optional<std::wstring> GetProcessId_8(DWORD dwProcessId) const;
|
||||
std::wstring GetProcessId(DWORD dwProcessId) const;
|
||||
std::optional<std::wstring> GetPwaAppId(const std::wstring& windowAumid) const;
|
||||
std::wstring SearchPwaName(const std::wstring& pwaAppId, const std::wstring& windowAumid) const;
|
||||
std::optional<std::wstring> SearchPwaAppId(const std::wstring& windowAumid) const;
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "pch.h"
|
||||
#include "WorkspacesData.h"
|
||||
|
||||
#include <common/SettingsAPI/settings_helpers.h>
|
||||
|
||||
#include <workspaces-common/GuidUtils.h>
|
||||
@@ -157,6 +156,7 @@ namespace WorkspacesData
|
||||
|
||||
result.isMaximized = json.GetNamedBoolean(NonLocalizable::MaximizedID);
|
||||
result.isMinimized = json.GetNamedBoolean(NonLocalizable::MinimizedID);
|
||||
|
||||
result.monitor = static_cast<int>(json.GetNamedNumber(NonLocalizable::MonitorID));
|
||||
if (json.HasKey(NonLocalizable::PositionID))
|
||||
{
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
<ClInclude Include="LaunchingStateEnum.h" />
|
||||
<ClInclude Include="LaunchingStatus.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="PwaHelper.h" />
|
||||
<ClInclude Include="Result.h" />
|
||||
<ClInclude Include="utils.h" />
|
||||
<ClInclude Include="WorkspacesData.h" />
|
||||
@@ -51,6 +52,7 @@
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PwaHelper.cpp" />
|
||||
<ClCompile Include="two_way_pipe_message_ipc.cpp" />
|
||||
<ClCompile Include="WorkspacesData.cpp" />
|
||||
<ClCompile Include="trace.cpp" />
|
||||
@@ -70,6 +72,7 @@
|
||||
<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>
|
||||
@@ -77,5 +80,6 @@
|
||||
</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>
|
||||
@@ -41,6 +41,9 @@
|
||||
<ClInclude Include="LaunchingStatus.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="PwaHelper.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
@@ -67,6 +70,9 @@
|
||||
<ClCompile Include="LaunchingStatus.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PwaHelper.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?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,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;
|
||||
};
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <workspaces-common/WindowFilter.h>
|
||||
|
||||
#include <WorkspacesLib/AppUtils.h>
|
||||
#include <PwaHelper.h>
|
||||
#include <WorkspacesLib/PwaHelper.h>
|
||||
|
||||
#pragma comment(lib, "ntdll.lib")
|
||||
|
||||
@@ -18,8 +18,6 @@ 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";
|
||||
}
|
||||
|
||||
bool IsProcessElevated(DWORD processID)
|
||||
@@ -40,19 +38,9 @@ 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{};
|
||||
Utils::PwaHelper pwaHelper{};
|
||||
std::vector<WorkspacesData::WorkspacesProject::Application> apps{};
|
||||
|
||||
auto installedApps = Utils::Apps::GetAppsList();
|
||||
@@ -128,47 +116,7 @@ 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")";
|
||||
}
|
||||
}
|
||||
}
|
||||
pwaHelper.UpdatePwaApp(&data.value(), window);
|
||||
|
||||
bool isMinimized = WindowUtils::IsMinimized(window);
|
||||
unsigned int monitorNumber = getMonitorNumberFromWindowHandle(window);
|
||||
@@ -184,12 +132,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,
|
||||
.pwaAppId = data.value().pwaAppId,
|
||||
.commandLineArgs = L"",
|
||||
.isElevated = IsProcessElevated(pid),
|
||||
.canLaunchElevated = data.value().canLaunchElevated,
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -12,9 +12,18 @@
|
||||
#include <workspaces-common/WindowUtils.h>
|
||||
|
||||
#include <WindowProperties/WorkspacesWindowPropertyUtils.h>
|
||||
#include <WorkspacesLib/PwaHelper.h>
|
||||
|
||||
namespace FancyZones
|
||||
namespace PlacementHelper
|
||||
{
|
||||
// When calculating the coordinates difference (== 'distance') between 2 windows, there are additional values added to the real distance
|
||||
// if both windows are minimized, the 'distance' is 0, the minimal value, this is the best match, we prefer this 'pairing'
|
||||
// if both are in normal state (non-minimized), we add 1 to the calculated 'distance', this is the 2nd best match
|
||||
// if one window is minimized and the other is maximized, we add a high value (10.000) to the result as
|
||||
// this case is the least desired match, we want this pairing (matching) only if there is no other possibility left
|
||||
const int PlacementDistanceAdditionBothNormal = 1;
|
||||
const int PlacementDistanceAdditionNormalAndMinimized = 10000;
|
||||
|
||||
inline void ScreenToWorkAreaCoords(HWND window, HMONITOR monitor, RECT& rect)
|
||||
{
|
||||
MONITORINFOEXW monitorInfo{ sizeof(MONITORINFOEXW) };
|
||||
@@ -52,14 +61,7 @@ namespace FancyZones
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((placement.showCmd != SW_SHOWMINIMIZED) &&
|
||||
(placement.showCmd != SW_MINIMIZE))
|
||||
{
|
||||
if (placement.showCmd == SW_SHOWMAXIMIZED)
|
||||
placement.flags &= ~WPF_RESTORETOMAXIMIZED;
|
||||
|
||||
placement.showCmd = SW_RESTORE;
|
||||
}
|
||||
placement.showCmd = SW_RESTORE;
|
||||
|
||||
ScreenToWorkAreaCoords(window, monitor, rect);
|
||||
placement.rcNormalPosition = rect;
|
||||
@@ -91,18 +93,191 @@ namespace FancyZones
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int CalculateDistance(const WorkspacesData::WorkspacesProject::Application& app, HWND window)
|
||||
{
|
||||
WINDOWPLACEMENT placement{};
|
||||
::GetWindowPlacement(window, &placement);
|
||||
|
||||
if (app.isMinimized && (placement.showCmd == SW_SHOWMINIMIZED))
|
||||
{
|
||||
// The most preferred case: both windows are minimized. The 'distance' between these 2 windows is 0, the lowest value
|
||||
return 0;
|
||||
}
|
||||
|
||||
int placementDiffPenalty = PlacementDistanceAdditionBothNormal;
|
||||
if (app.isMinimized || (placement.showCmd == SW_SHOWMINIMIZED))
|
||||
{
|
||||
// The least preferred case: one window is minimized the other one isn't.
|
||||
// We add a high number to the real distance, as we want this 2 windows be matched only if there is no other match
|
||||
placementDiffPenalty = PlacementDistanceAdditionNormalAndMinimized;
|
||||
}
|
||||
|
||||
RECT windowPosition;
|
||||
GetWindowRect(window, &windowPosition);
|
||||
DPIAware::InverseConvert(MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY), windowPosition);
|
||||
|
||||
return placementDiffPenalty + abs(app.position.x - windowPosition.left) + abs(app.position.y - windowPosition.top) + abs(app.position.x + app.position.width - windowPosition.right) + abs(app.position.y + app.position.height - windowPosition.bottom);
|
||||
}
|
||||
}
|
||||
|
||||
bool WindowArranger::TryMoveWindow(const WorkspacesData::WorkspacesProject::Application& app, HWND windowToMove)
|
||||
{
|
||||
Logger::info(L"The app {} is found at launch, moving it", app.name);
|
||||
auto appState = m_launchingStatus.Get(app);
|
||||
if (!appState.has_value())
|
||||
{
|
||||
Logger::info(L"The app {} is not found in the map of apps", app.name);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool success = moveWindow(windowToMove, app);
|
||||
if (success)
|
||||
{
|
||||
m_launchingStatus.Update(appState.value().application, windowToMove, LaunchingState::LaunchedAndMoved);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::info(L"Failed to move the existing app {} ", app.name);
|
||||
m_launchingStatus.Update(appState.value().application, windowToMove, LaunchingState::Failed);
|
||||
}
|
||||
|
||||
auto updatedState = m_launchingStatus.Get(app);
|
||||
if (updatedState.has_value())
|
||||
{
|
||||
m_ipcHelper.send(WorkspacesData::AppLaunchInfoJSON::ToJson(updatedState.value()).ToString().c_str());
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
std::optional<WindowWithDistance> WindowArranger::GetNearestWindow(const WorkspacesData::WorkspacesProject::Application& app, const std::vector<HWND>& movedWindows, Utils::PwaHelper& pwaHelper)
|
||||
{
|
||||
std::optional<Utils::Apps::AppData> appDataNearest = std::nullopt;
|
||||
WindowWithDistance nearestWindowWithDistance{};
|
||||
|
||||
for (HWND window : m_windowsBefore)
|
||||
{
|
||||
if (std::find(movedWindows.begin(), movedWindows.end(), window) != movedWindows.end())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::wstring processPath = get_process_path(window);
|
||||
if (processPath.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
DWORD pid{};
|
||||
GetWindowThreadProcessId(window, &pid);
|
||||
|
||||
auto data = Utils::Apps::GetApp(processPath, pid, m_installedApps);
|
||||
if (!data.has_value())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
pwaHelper.UpdatePwaApp(&data.value(), window);
|
||||
|
||||
if ((app.name == data.value().name || app.path == data.value().installPath) && (app.pwaAppId == data.value().pwaAppId))
|
||||
{
|
||||
if (!appDataNearest.has_value())
|
||||
{
|
||||
appDataNearest = data;
|
||||
nearestWindowWithDistance.distance = PlacementHelper::CalculateDistance(app, window);
|
||||
nearestWindowWithDistance.window = window;
|
||||
}
|
||||
else
|
||||
{
|
||||
int currentDistance = PlacementHelper::CalculateDistance(app, window);
|
||||
if (currentDistance < nearestWindowWithDistance.distance)
|
||||
{
|
||||
appDataNearest = data;
|
||||
nearestWindowWithDistance.distance = currentDistance;
|
||||
nearestWindowWithDistance.window = window;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (appDataNearest.has_value())
|
||||
{
|
||||
return nearestWindowWithDistance;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
WindowArranger::WindowArranger(WorkspacesData::WorkspacesProject project) :
|
||||
m_project(project),
|
||||
m_windowsBefore(WindowEnumerator::Enumerate(WindowFilter::Filter)),
|
||||
m_monitors(MonitorUtils::IdentifyMonitors()),
|
||||
m_installedApps(Utils::Apps::GetAppsList()),
|
||||
//m_windowCreationHandler(std::bind(&WindowArranger::onWindowCreated, this, std::placeholders::_1)),
|
||||
m_ipcHelper(IPCHelperStrings::WindowArrangerPipeName, IPCHelperStrings::LauncherArrangerPipeName, std::bind(&WindowArranger::receiveIpcMessage, this, std::placeholders::_1)),
|
||||
m_launchingStatus(m_project)
|
||||
{
|
||||
if (project.moveExistingWindows)
|
||||
{
|
||||
Logger::info(L"Moving existing windows");
|
||||
bool isMovePhase = true;
|
||||
bool movedAny = false;
|
||||
std::vector<HWND> movedWindows;
|
||||
std::vector<WorkspacesData::WorkspacesProject::Application> movedApps;
|
||||
Utils::PwaHelper pwaHelper{};
|
||||
|
||||
while (isMovePhase)
|
||||
{
|
||||
isMovePhase = false;
|
||||
int minDistance = INT_MAX;
|
||||
WorkspacesData::WorkspacesProject::Application appToMove;
|
||||
HWND windowToMove = NULL;
|
||||
for (auto& app : project.apps)
|
||||
{
|
||||
// move the apps which are set to "Move-If-Exists" and are already present (launched, running)
|
||||
if (std::find(movedApps.begin(), movedApps.end(), app) != movedApps.end())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
std::optional<WindowWithDistance> nearestWindowWithDistance;
|
||||
nearestWindowWithDistance = GetNearestWindow(app, movedWindows, pwaHelper);
|
||||
if (nearestWindowWithDistance.has_value())
|
||||
{
|
||||
if (nearestWindowWithDistance.value().distance < minDistance)
|
||||
{
|
||||
minDistance = nearestWindowWithDistance.value().distance;
|
||||
appToMove = app;
|
||||
windowToMove = nearestWindowWithDistance.value().window;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::info(L"The app {} is not found at launch, cannot be moved, has to be started", app.name);
|
||||
movedApps.push_back(app);
|
||||
}
|
||||
}
|
||||
if (minDistance < INT_MAX)
|
||||
{
|
||||
isMovePhase = true;
|
||||
movedAny = true;
|
||||
bool success = TryMoveWindow(appToMove, windowToMove);
|
||||
movedApps.push_back(appToMove);
|
||||
if (success)
|
||||
{
|
||||
movedWindows.push_back(windowToMove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (movedAny)
|
||||
{
|
||||
// Wait if there were moved windows. This message might not arrive if sending immediately after the last "moved" message (status update)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
||||
Logger::info(L"Finished moving existing windows");
|
||||
}
|
||||
|
||||
m_ipcHelper.send(L"ready");
|
||||
|
||||
const long maxLaunchingWaitingTime = 10000, maxRepositionWaitingTime = 3000, ms = 300;
|
||||
@@ -138,27 +313,17 @@ WindowArranger::WindowArranger(WorkspacesData::WorkspacesProject project) :
|
||||
}
|
||||
}
|
||||
|
||||
//void WindowArranger::onWindowCreated(HWND window)
|
||||
//{
|
||||
// if (!WindowFilter::Filter(window))
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// processWindow(window);
|
||||
//}
|
||||
|
||||
void WindowArranger::processWindows(bool processAll)
|
||||
{
|
||||
std::vector<HWND> windows = WindowEnumerator::Enumerate(WindowFilter::Filter);
|
||||
|
||||
|
||||
if (!processAll)
|
||||
{
|
||||
std::vector<HWND> windowsDiff{};
|
||||
std::copy_if(windows.begin(), windows.end(), std::back_inserter(windowsDiff), [&](HWND window) { return std::find(m_windowsBefore.begin(), m_windowsBefore.end(), window) == m_windowsBefore.end(); });
|
||||
windows = windowsDiff;
|
||||
}
|
||||
|
||||
|
||||
for (HWND window : windows)
|
||||
{
|
||||
processWindow(window);
|
||||
@@ -194,12 +359,11 @@ void WindowArranger::processWindow(HWND window)
|
||||
}
|
||||
|
||||
const auto& apps = m_launchingStatus.Get();
|
||||
auto iter = std::find_if(apps.begin(), apps.end(), [&](const auto& val)
|
||||
{
|
||||
return val.second.state == LaunchingState::Launched &&
|
||||
!val.second.window &&
|
||||
(val.first.name == data.value().name || val.first.path == data.value().installPath);
|
||||
});
|
||||
auto iter = std::find_if(apps.begin(), apps.end(), [&](const auto& val) {
|
||||
return val.second.state == LaunchingState::Launched &&
|
||||
!val.second.window &&
|
||||
(val.first.name == data.value().name || val.first.path == data.value().installPath);
|
||||
});
|
||||
|
||||
if (iter == apps.end())
|
||||
{
|
||||
@@ -258,7 +422,7 @@ bool WindowArranger::moveWindow(HWND window, const WorkspacesData::WorkspacesPro
|
||||
rect.top = static_cast<long>(std::round(rect.top * mult));
|
||||
rect.bottom = static_cast<long>(std::round(rect.bottom * mult));
|
||||
|
||||
if (FancyZones::SizeWindowToRect(window, currentMonitor, launchMinimized, launchMaximized, rect))
|
||||
if (PlacementHelper::SizeWindowToRect(window, currentMonitor, launchMinimized, launchMaximized, rect))
|
||||
{
|
||||
WorkspacesWindowProperties::StampWorkspacesLaunchedProperty(window);
|
||||
Logger::trace(L"Placed {} to ({},{}) [{}x{}]", app.name, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
|
||||
|
||||
@@ -5,8 +5,15 @@
|
||||
#include <WorkspacesLib/AppUtils.h>
|
||||
#include <WorkspacesLib/IPCHelper.h>
|
||||
#include <WorkspacesLib/LaunchingStatus.h>
|
||||
#include <WorkspacesLib/PwaHelper.h>
|
||||
#include <WorkspacesLib/WorkspacesData.h>
|
||||
|
||||
struct WindowWithDistance
|
||||
{
|
||||
int distance;
|
||||
HWND window;
|
||||
};
|
||||
|
||||
class WindowArranger
|
||||
{
|
||||
public:
|
||||
@@ -21,7 +28,9 @@ private:
|
||||
//const WindowCreationHandler m_windowCreationHandler;
|
||||
IPCHelper m_ipcHelper;
|
||||
LaunchingStatus m_launchingStatus;
|
||||
|
||||
std::optional<WindowWithDistance> GetNearestWindow(const WorkspacesData::WorkspacesProject::Application& app, const std::vector<HWND>& movedWindows, Utils::PwaHelper& pwaHelper);
|
||||
bool TryMoveWindow(const WorkspacesData::WorkspacesProject::Application& app, HWND windowToMove);
|
||||
|
||||
//void onWindowCreated(HWND window);
|
||||
void processWindows(bool processAll);
|
||||
void processWindow(HWND window);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) Brice Lambson
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
// This file is used by Code Analysis to maintain SuppressMessage
|
||||
// attributes that are applied to this project.
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using ImageResizer.Properties;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) Brice Lambson
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) Brice Lambson
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) Brice Lambson
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
namespace System.Windows.Media.Imaging
|
||||
{
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) Brice Lambson
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
namespace System.Collections.Generic
|
||||
{
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) Brice Lambson
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
namespace System
|
||||
{
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) Brice Lambson
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) Brice Lambson
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) Brice Lambson
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
namespace ImageResizer.Models
|
||||
{
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// Copyright (c) Brice Lambson
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
// Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
namespace ImageResizer.Models
|
||||
{
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) Brice Lambson
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// Copyright (c) Brice Lambson
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
// Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// Copyright (c) Brice Lambson
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
// Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
namespace ImageResizer.Models
|
||||
{
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) Brice Lambson
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) Brice Lambson
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) Brice Lambson
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) Brice Lambson
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) Brice Lambson
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) Brice Lambson
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System;
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) Brice Lambson
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) Brice Lambson
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
namespace ImageResizer.ViewModels
|
||||
{
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) Brice Lambson
|
||||
#pragma warning disable IDE0073
|
||||
// Copyright (c) Brice Lambson
|
||||
// The Brice Lambson licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
||||
#pragma warning restore IDE0073
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user