Compare commits

..

1 Commits

Author SHA1 Message Date
Shawn Yuan
78ed5c7966 Add new pipeline file 2024-11-28 11:49:56 +08:00
152 changed files with 982 additions and 1980 deletions

View File

@@ -226,8 +226,3 @@ arcosh
dbus
anypass
gpg
# .NET
AOT
Aot

View File

@@ -44,7 +44,6 @@ Bartosz
betadele
betsegaw
bricelam
bsky
CCcat
Chinh
chrdavis
@@ -54,7 +53,6 @@ Coplen
craigloewen
crutkas
damienleroy
daverayment
davidegiacometti
debian
Deibisu
@@ -66,7 +64,6 @@ Essey
ethanfangg
ferraridavide
frankychen
Gaarden
gaardmark
gabime
Galaxi
@@ -74,7 +71,6 @@ Garside
Gershaft
Giordani
Gokce
gordon
grzhan
Guo
hanselman
@@ -86,7 +82,6 @@ Horvalds
Howett
htcfreek
Huynh
Ionut
Jaswal
jefflord
Jordi
@@ -95,13 +90,11 @@ Kairu
Kamra
Kantarci
Karthick
kaylacinnamon
kevinguo
Krigun
Lambson
Laute
laviusmotileng
Leilei
Luecking
Mahalingam
Markovic
@@ -117,7 +110,6 @@ nathancartlidge
Nemeth
nielslaute
oldnewthing
onegreatworld
palenshus
pedrolamas
peteblois
@@ -128,7 +120,6 @@ Pylyp
quachpas
Quriz
randyrants
rayment
ricardosantos
riri
ritchielawrence
@@ -157,7 +148,6 @@ waaverecords
Whuihuan
Xpg
ycv
yeelam
Yuniardi
yuyoyuppe
Zeol

View File

@@ -667,7 +667,6 @@ Iindex
IJson
Ijwhost
IKs
iljxck
ILogon
IMAGEHLP
IMAGERESIZERCONTEXTMENU
@@ -771,7 +770,6 @@ lastcodeanalysissucceeded
Lastdevice
LASTEXITCODE
LAYOUTRTL
lcb
LCIDTo
lcl
Lclean

View File

View File

@@ -28,6 +28,7 @@ parameters:
type: object
default:
- x64
- arm64
- name: enableMsBuildCaching
type: boolean
displayName: "Enable MSBuild Caching"

View File

@@ -1,39 +0,0 @@
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)

View File

@@ -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,54 +26,43 @@ 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 }}
- 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}}
# jobs:
# - template: job-test-project.yml
# parameters:
# platform: ${{ platform }}
# configuration: Release
- stage: Fuzz ${platform}
displayName: Fuzz ${{ platform }}
- ${{ if eq(parameters.runTests, true) }}:
- stage: Test_${{ platform }}
displayName: Test ${{ platform }}
dependsOn:
- Build_${platform}
- Build_${{platform}}
jobs:
- template: job-fuzz.yml
- template: job-test-project.yml
parameters:
platform: ${{ platform }}
configuration: Release
inputArtifactStem: ""

View File

@@ -9,18 +9,12 @@ 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.
@@ -48,9 +42,6 @@ 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.
@@ -163,23 +154,18 @@ Other contributors:
## PowerToys core team
- [@crutkas](https://github.com/crutkas/) - Clint Rutkas - Lead
- [@cinnamon-msft](https://github.com/cinnamon-msft) - Kayla Cinnamon - Lead
- [@ethanfangg](https://github.com/ethanfangg) - Ethan Fang - Product Manager
- [@ethanfangg](https://github.com/ethanfangg) - Ethan Fang - Lead
- [@cinnamon-msft](https://github.com/cinnamon-msft) - Kayla Cinnamon - 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
@@ -190,4 +176,3 @@ 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

View File

@@ -34,7 +34,6 @@
<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. -->

View File

@@ -171,7 +171,6 @@ 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
@@ -635,8 +634,6 @@ 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
@@ -2809,18 +2806,6 @@ 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
@@ -3054,7 +3039,6 @@ 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}

View File

@@ -1,6 +1,6 @@
# Microsoft PowerToys
![Hero image for Microsoft PowerToys](doc/images/overview/PT_holiday_hero_image.png)
![Hero image for Microsoft PowerToys](doc/images/overview/PT_hero_image.png)
[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,12 +20,6 @@ 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

View File

@@ -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 [`customTokenThemeRules.js`](/src/Monaco/customTokenThemeRules.js):
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):
```javascript
{token: 'token-name', foreground: 'ff0000'}
```

View File

@@ -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 of version 1, 3, 4, 5, and 7.
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.
![Image of Value Generator plugin](/doc/images/launcher/plugin/community.valuegenerator.png)
@@ -34,10 +34,7 @@ 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, 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
- 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
- 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 503 KiB

View File

@@ -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", "srt.js", "monacoSpecialLanguages.js", "customTokenThemeRules.js", "*.pri")
$fileInclusionList = @("*.dll", "*.exe", "*.json", "*.msix", "*.png", "*.gif", "*.ico", "*.cur", "*.svg", "index.html", "reg.js", "gitignore.js", "monacoSpecialLanguages.js", "customTokenColors.js", "*.pri")
$dllsToIgnore = @("System.CodeDom.dll", "WindowsBase.dll")

View File

@@ -46,28 +46,32 @@ 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
@@ -92,176 +96,4 @@ end_of_line = crlf
dotnet_diagnostic.IDE0065.severity = none
# IDE0009: Add this or Me qualification
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
dotnet_diagnostic.IDE0009.severity = none

View File

@@ -1,9 +0,0 @@
<?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>

View File

@@ -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\customTokenThemeRules.js">
<Link>Assets\Monaco\customTokenThemeRules.js</Link>
<None Include="$(MSBuildThisFileDirectory)\Monaco\customTokenColors.js">
<Link>Assets\Monaco\customTokenColors.js</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)\Monaco\monacoSpecialLanguages.js">

View File

@@ -6,7 +6,7 @@
root: [
[/^#.*$/, 'comment'],
[/.*((?<!(^|\/))\*\*.*|\*\*(?!(\/|$))).*/, 'invalid'],
[/((?:^!\s*(?:\\\s|\S)+)?)((?:^\s*(?:\\\s|\S)+)?)((?:\s+(?:\\\s|\S)+)*)/, ['custom-negation', 'tag', 'invalid']]
[/((?:^!\s*(?:\\\s|\S)+)?)((?:^\s*(?:\\\s|\S)+)?)((?:\s+(?:\\\s|\S)+)*)/, ['custom-gitignore.negation', 'tag', 'invalid']]
]
}
};

View File

@@ -1,29 +0,0 @@
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']
]
}
};
}

View File

@@ -0,0 +1,3 @@
export const customTokenColors = [
{token: 'custom-gitignore.negation', foreground: 'c00ce0'}
];

View File

@@ -1,3 +0,0 @@
export const customTokenThemeRules = [
{token: 'custom-negation.gitignore', foreground: 'c00ce0'}
];

View File

@@ -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 { customTokenThemeRules } from 'http://[[PT_URL]]/customTokenThemeRules.js';
import { customTokenColors } from 'http://[[PT_URL]]/customTokenColors.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: customTokenThemeRules,
rules: customTokenColors,
colors: {} // `colors` is a required attribute
});

View File

@@ -2,20 +2,18 @@
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", ".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);
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)
}
// Language definitions taken from Monaco source code

File diff suppressed because one or more lines are too long

View File

@@ -109,24 +109,6 @@ 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);

View File

@@ -15,7 +15,6 @@ 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

View File

@@ -241,12 +241,10 @@ 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";

View File

@@ -1,28 +0,0 @@
<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>

View File

@@ -1,19 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using 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());
}
}
}

View File

@@ -1,44 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 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
}
}
}

View File

@@ -1,5 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]

View File

@@ -1,40 +0,0 @@
{
"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"
]
}
]
}

View File

@@ -33,7 +33,6 @@ 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
{

View File

@@ -215,10 +215,10 @@ namespace newplus::utilities
inline bool is_desktop_folder(const std::filesystem::path target_fullpath)
{
TCHAR desktop_path[MAX_PATH];
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, desktop_path)))
TCHAR desktopPath[MAX_PATH];
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, desktopPath)))
{
return StrCmpIW(target_fullpath.c_str(), desktop_path) == 0;
return StrCmpIW(target_fullpath.c_str(), desktopPath) == 0;
}
return false;
}
@@ -376,10 +376,8 @@ 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);
@@ -394,12 +392,13 @@ 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;
}

View File

@@ -3,6 +3,9 @@
// 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

View File

@@ -8,7 +8,13 @@ namespace WorkspacesEditor.Data
{
public class TempProjectData : ProjectData
{
public static string File => FolderUtils.DataFolder() + "\\temp-workspaces.json";
public static string File
{
get
{
return FolderUtils.DataFolder() + "\\temp-workspaces.json";
}
}
public static void DeleteTempFile()
{

View File

@@ -3,6 +3,8 @@
// 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;
@@ -12,7 +14,13 @@ namespace WorkspacesEditor.Data
{
public class WorkspacesData : WorkspacesEditorData<WorkspacesListWrapper>
{
public string File => FolderUtils.DataFolder() + "\\workspaces.json";
public string File
{
get
{
return FolderUtils.DataFolder() + "\\workspaces.json";
}
}
public struct WorkspacesListWrapper
{

View File

@@ -3,24 +3,28 @@
// See the LICENSE file in the project root for more information.
using System.Text.Json;
using WorkspacesEditor.Utils;
namespace WorkspacesEditor.Data
namespace Workspaces.Data
{
public class WorkspacesEditorData<T>
{
protected JsonSerializerOptions JsonOptions
{
get => new()
get
{
PropertyNamingPolicy = new DashCaseNamingPolicy(),
WriteIndented = true,
};
return new JsonSerializerOptions
{
PropertyNamingPolicy = new DashCaseNamingPolicy(),
WriteIndented = true,
};
}
}
public T Read(string file)
{
IOUtils ioUtils = new();
IOUtils ioUtils = new IOUtils();
string data = ioUtils.ReadFile(file);
return JsonSerializer.Deserialize<T>(data, JsonOptions);
}

View File

@@ -8,7 +8,7 @@
xmlns:ui="http://schemas.modernwpf.com/2019"
x:Name="WorkspacesMainWindow"
Title="{x:Static props:Resources.MainTitle}"
MinWidth="750"
MinWidth="700"
MinHeight="680"
ui:TitleBar.Background="{DynamicResource PrimaryBackgroundBrush}"
ui:TitleBar.InactiveBackground="{DynamicResource TertiaryBackgroundBrush}"

View File

@@ -18,7 +18,14 @@ namespace WorkspacesEditor.Models
public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
{
return item is MonitorHeaderRow ? HeaderTemplate : AppTemplate;
if (item is MonitorHeaderRow)
{
return HeaderTemplate;
}
else
{
return AppTemplate;
}
}
}
}

View File

@@ -1,22 +1,25 @@
// 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;
@@ -76,7 +79,7 @@ namespace WorkspacesEditor.Models
return left.X != right.X || left.Y != right.Y || left.Width != right.Width || left.Height != right.Height;
}
public override readonly bool Equals(object obj)
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
{
@@ -87,7 +90,7 @@ namespace WorkspacesEditor.Models
return X == pos.X && Y == pos.Y && Width == pos.Width && Height == pos.Height;
}
public override readonly int GetHashCode()
public override int GetHashCode()
{
return base.GetHashCode();
}
@@ -133,24 +136,36 @@ namespace WorkspacesEditor.Models
}
}
public bool Minimized { get; set; }
private bool _minimized;
public bool Maximized { get; set; }
public bool EditPositionEnabled => !Minimized && !Maximized;
public int PositionComboboxIndex
public bool Minimized
{
get => Maximized ? (int)WindowPositionKind.Maximized : Minimized ? (int)WindowPositionKind.Minimized : (int)WindowPositionKind.Custom;
get => _minimized;
set
{
Maximized = value == (int)WindowPositionKind.Maximized;
Minimized = value == (int)WindowPositionKind.Minimized;
_minimized = value;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(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
@@ -168,7 +183,7 @@ namespace WorkspacesEditor.Models
}
}
public bool IsAppMainParamVisible => !string.IsNullOrWhiteSpace(_appMainParams);
public bool IsAppMainParamVisible { get => !string.IsNullOrWhiteSpace(_appMainParams); }
[JsonIgnore]
public bool IsHighlighted { get; set; }
@@ -177,7 +192,13 @@ namespace WorkspacesEditor.Models
public int RepeatIndex { get; set; }
[JsonIgnore]
public string RepeatIndexString => RepeatIndex <= 1 ? string.Empty : RepeatIndex.ToString(CultureInfo.InvariantCulture);
public string RepeatIndexString
{
get
{
return RepeatIndex <= 1 ? string.Empty : RepeatIndex.ToString(CultureInfo.InvariantCulture);
}
}
private WindowPosition _position;
@@ -221,7 +242,10 @@ namespace WorkspacesEditor.Models
{
get
{
_monitorSetup ??= Parent.GetMonitorForApp(this);
if (_monitorSetup == null)
{
_monitorSetup = Parent.GetMonitorForApp(this);
}
return _monitorSetup;
}
@@ -247,7 +271,7 @@ namespace WorkspacesEditor.Models
}
}
public string DeleteButtonContent => _isIncluded ? Properties.Resources.Delete : Properties.Resources.AddBack;
public string DeleteButtonContent { get => _isIncluded ? Properties.Resources.Delete : Properties.Resources.AddBack; }
private bool _isIncluded = true;
@@ -274,5 +298,15 @@ namespace WorkspacesEditor.Models
CommandLineArguments = newCommandLineValue;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(AppMainParams)));
}
internal void MaximizedChecked()
{
Minimized = false;
}
internal void MinimizedChecked()
{
Maximized = false;
}
}
}

View File

@@ -6,18 +6,28 @@ using System.Windows;
namespace WorkspacesEditor.Models
{
public class Monitor(string monitorName, string monitorInstanceId, int number, int dpi, Rect dpiAwareBounds, Rect dpiUnawareBounds)
public class Monitor
{
public string MonitorName { get; private set; } = monitorName;
public string MonitorName { get; private set; }
public string MonitorInstanceId { get; private set; } = monitorInstanceId;
public string MonitorInstanceId { get; private set; }
public int MonitorNumber { get; private set; } = number;
public int MonitorNumber { get; private set; }
public int Dpi { get; private set; } = dpi;
public int Dpi { get; private set; }
public Rect MonitorDpiUnawareBounds { get; private set; } = dpiUnawareBounds;
public Rect MonitorDpiUnawareBounds { get; private set; }
public Rect MonitorDpiAwareBounds { get; private set; } = dpiAwareBounds;
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;
}
}
}

View File

@@ -16,9 +16,9 @@ namespace WorkspacesEditor.Models
PropertyChanged?.Invoke(this, e);
}
public string MonitorInfo => MonitorName;
public string MonitorInfo { get => MonitorName; }
public string MonitorInfoWithResolution => $"{MonitorName} {MonitorDpiAwareBounds.Width}x{MonitorDpiAwareBounds.Height}";
public string MonitorInfoWithResolution { get => $"{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)

View File

@@ -29,7 +29,10 @@ namespace WorkspacesEditor.Models
public string Name
{
get => _name;
get
{
return _name;
}
set
{
@@ -65,7 +68,8 @@ namespace WorkspacesEditor.Models
DateTime lastLaunchDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(LastLaunchedTime);
TimeSpan ts = DateTime.UtcNow - lastLaunchDateTime;
var now = DateTime.UtcNow.Ticks;
var ts = DateTime.UtcNow - lastLaunchDateTime;
double delta = Math.Abs(ts.TotalSeconds);
if (delta < 1 * MINUTE)
@@ -116,7 +120,10 @@ namespace WorkspacesEditor.Models
}
}
public bool CanBeSaved => Name.Length > 0 && Applications.Count > 0;
public bool CanBeSaved
{
get => Name.Length > 0 && Applications.Count > 0;
}
private bool _isRevertEnabled;
@@ -138,7 +145,10 @@ namespace WorkspacesEditor.Models
[JsonIgnore]
public bool IsPopupVisible
{
get => _isPopupVisible;
get
{
return _isPopupVisible;
}
set
{
@@ -153,11 +163,11 @@ namespace WorkspacesEditor.Models
{
get
{
List<object> applicationsListed = [];
List<object> applicationsListed = new List<object>();
ILookup<MonitorSetup, Application> apps = Applications.Where(x => !x.Minimized).ToLookup(x => x.MonitorSetup);
foreach (IGrouping<MonitorSetup, Application> appItem in apps.OrderBy(x => x.Key.MonitorDpiUnawareBounds.Left).ThenBy(x => x.Key.MonitorDpiUnawareBounds.Top))
foreach (var appItem in apps.OrderBy(x => x.Key.MonitorDpiUnawareBounds.Left).ThenBy(x => x.Key.MonitorDpiUnawareBounds.Top))
{
MonitorHeaderRow headerRow = new() { MonitorName = "Screen " + appItem.Key.MonitorNumber, SelectString = Properties.Resources.SelectAllAppsOnMonitor + " " + appItem.Key.MonitorInfo };
MonitorHeaderRow headerRow = new MonitorHeaderRow { MonitorName = "Screen " + appItem.Key.MonitorNumber, SelectString = Properties.Resources.SelectAllAppsOnMonitor + " " + appItem.Key.MonitorInfo };
applicationsListed.Add(headerRow);
foreach (Application app in appItem)
{
@@ -165,10 +175,10 @@ namespace WorkspacesEditor.Models
}
}
IEnumerable<Application> minimizedApps = Applications.Where(x => x.Minimized);
var minimizedApps = Applications.Where(x => x.Minimized);
if (minimizedApps.Any())
{
MonitorHeaderRow headerRow = new() { MonitorName = Properties.Resources.Minimized_Apps, SelectString = Properties.Resources.SelectAllMinimizedApps };
MonitorHeaderRow headerRow = new MonitorHeaderRow { MonitorName = Properties.Resources.Minimized_Apps, SelectString = Properties.Resources.SelectAllMinimizedApps };
applicationsListed.Add(headerRow);
foreach (Application app in minimizedApps)
{
@@ -209,17 +219,17 @@ namespace WorkspacesEditor.Models
int screenIndex = 1;
Monitors = [];
foreach (MonitorSetup item in selectedProject.Monitors.OrderBy(x => x.MonitorDpiAwareBounds.Left).ThenBy(x => x.MonitorDpiAwareBounds.Top))
Monitors = new List<MonitorSetup>();
foreach (var item in selectedProject.Monitors.OrderBy(x => x.MonitorDpiAwareBounds.Left).ThenBy(x => x.MonitorDpiAwareBounds.Top))
{
Monitors.Add(item);
screenIndex++;
}
Applications = [];
foreach (Application item in selectedProject.Applications)
Applications = new List<Application>();
foreach (var item in selectedProject.Applications)
{
Application newApp = new(item);
Application newApp = new Application(item);
newApp.Parent = this;
newApp.InitializationFinished();
Applications.Add(newApp);
@@ -234,14 +244,14 @@ namespace WorkspacesEditor.Models
LastLaunchedTime = project.LastLaunchedTime;
IsShortcutNeeded = project.IsShortcutNeeded;
MoveExistingWindows = project.MoveExistingWindows;
Monitors = [];
Applications = [];
Monitors = new List<MonitorSetup>() { };
Applications = new List<Models.Application> { };
foreach (ProjectData.ApplicationWrapper app in project.Applications)
foreach (var app in project.Applications)
{
Models.Application newApp = new()
Models.Application newApp = new Models.Application()
{
Id = string.IsNullOrEmpty(app.Id) ? $"{{{Guid.NewGuid()}}}" : app.Id,
Id = string.IsNullOrEmpty(app.Id) ? $"{{{Guid.NewGuid().ToString()}}}" : app.Id,
AppName = app.Application,
AppPath = app.ApplicationPath,
AppTitle = app.Title,
@@ -268,17 +278,20 @@ namespace WorkspacesEditor.Models
Applications.Add(newApp);
}
foreach (ProjectData.MonitorConfigurationWrapper monitor in project.MonitorConfiguration)
foreach (var monitor in project.MonitorConfiguration)
{
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);
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);
Monitors.Add(new MonitorSetup(monitor.Id, monitor.InstanceId, monitor.MonitorNumber, monitor.Dpi, dpiAware, dpiUnaware));
}
}
public BitmapImage PreviewIcons
{
get => _previewIcons;
get
{
return _previewIcons;
}
set
{
@@ -289,7 +302,10 @@ namespace WorkspacesEditor.Models
public BitmapImage PreviewImage
{
get => _previewImage;
get
{
return _previewImage;
}
set
{
@@ -300,7 +316,10 @@ namespace WorkspacesEditor.Models
public double PreviewImageWidth
{
get => _previewImageWidth;
get
{
return _previewImageWidth;
}
set
{
@@ -347,7 +366,6 @@ namespace WorkspacesEditor.Models
Id = other.Id;
Name = other.Name;
IsRevertEnabled = true;
MoveExistingWindows = other.MoveExistingWindows;
}
internal void CloseExpanders()
@@ -360,13 +378,13 @@ namespace WorkspacesEditor.Models
internal MonitorSetup GetMonitorForApp(Application app)
{
MonitorSetup monitorSetup = Monitors.Where(x => x.MonitorNumber == app.MonitorNumber).FirstOrDefault();
var 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);
MonitorSetup monitorCandidate = Monitors.Where(x =>
var monitorCandidate = Monitors.Where(x =>
(x.MonitorDpiUnawareBounds.Left < middleX) &&
(x.MonitorDpiUnawareBounds.Right > middleX) &&
(x.MonitorDpiUnawareBounds.Top < middleY) &&

View File

@@ -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,6 +78,15 @@ 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>
@@ -160,7 +169,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 {
@@ -177,15 +186,6 @@ 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 &amp; edit.
/// Looks up a localized string similar to Launch &amp; 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 existing windows.
/// Looks up a localized string similar to Move apps if present.
/// </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,15 +645,6 @@ 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>

View File

@@ -123,6 +123,9 @@
<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>
@@ -157,9 +160,6 @@
<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 existing windows</value>
<value>Move apps if present</value>
</data>
<data name="Name" xml:space="preserve">
<value>Name</value>
@@ -321,9 +321,6 @@
<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>

View File

@@ -20,21 +20,21 @@ namespace WorkspacesEditor.Utils
{
public class DrawHelper
{
private static readonly Font Font = new("Tahoma", 24);
private static readonly double Scale = 0.1;
private static Font font = new("Tahoma", 24);
private static 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 = [];
List<double> verticalGaps = [];
List<double> horizontalGaps = new List<double>();
List<double> verticalGaps = new List<double>();
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;
MonitorSetup monitor = project.GetMonitorForApp(app);
var monitor = project.GetMonitorForApp(app);
if (monitor == null)
{
// unrealistic case, there are no monitors at all in the workspace, use original rect
@@ -69,23 +69,22 @@ namespace WorkspacesEditor.Utils
}
}
Dictionary<string, int> repeatCounter = [];
Dictionary<string, int> repeatCounter = new Dictionary<string, int>();
IEnumerable<Application> appsIncluded = project.Applications.Where(x => x.IsIncluded);
var appsIncluded = project.Applications.Where(x => x.IsIncluded);
foreach (Application app in appsIncluded)
{
string appIdentifier = app.AppPath + app.PwaAppId;
if (repeatCounter.TryGetValue(appIdentifier, out int value))
if (repeatCounter.TryGetValue(app.AppPath + app.AppTitle, out int value))
{
repeatCounter[appIdentifier] = ++value;
repeatCounter[app.AppPath + app.AppTitle] = ++value;
}
else
{
repeatCounter.Add(appIdentifier, 1);
repeatCounter.Add(app.AppPath + app.AppTitle, 1);
}
app.RepeatIndex = repeatCounter[appIdentifier];
app.RepeatIndex = repeatCounter[app.AppPath + app.AppTitle];
}
foreach (Application app in project.Applications.Where(x => !x.IsIncluded))
@@ -114,7 +113,7 @@ namespace WorkspacesEditor.Utils
}
}
Bitmap previewBitmap = new(Scaled(bounds.Width + (verticalGaps.Count * gapWidth)), Scaled((bounds.Height * 1.2) + (horizontalGaps.Count * gapHeight)));
Bitmap previewBitmap = new Bitmap(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))
{
@@ -132,7 +131,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)));
}
IEnumerable<Application> appsToDraw = appsIncluded.Where(x => !x.Minimized);
var 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))
@@ -148,22 +147,24 @@ namespace WorkspacesEditor.Utils
}
// draw the minimized windows
Rectangle rectMinimized = new(0, Scaled((bounds.Height * 1.02) + (horizontalGaps.Count * gapHeight)), Scaled(bounds.Width + (verticalGaps.Count * gapWidth)), Scaled(bounds.Height * 0.18));
Rectangle rectMinimized = new Rectangle(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 MemoryStream memory = new();
previewBitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
using (var memory = new MemoryStream())
{
previewBitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
BitmapImage bitmapImage = new();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.Freeze();
var bitmapImage = new BitmapImage();
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)
@@ -193,7 +194,7 @@ namespace WorkspacesEditor.Utils
}
double iconSize = Math.Min(Math.Min(bounds.Width - 4, bounds.Height - 4), desiredIconSize);
Rectangle iconBounds = new((int)(bounds.Left + (bounds.Width / 2) - (iconSize / 2)), (int)(bounds.Top + (bounds.Height / 2) - (iconSize / 2)), (int)iconSize, (int)iconSize);
Rectangle iconBounds = new Rectangle((int)(bounds.Left + (bounds.Width / 2) - (iconSize / 2)), (int)(bounds.Top + (bounds.Height / 2) - (iconSize / 2)), (int)iconSize, (int)iconSize);
try
{
@@ -202,13 +203,13 @@ namespace WorkspacesEditor.Utils
{
string indexString = app.RepeatIndex.ToString(CultureInfo.InvariantCulture);
int indexSize = (int)(iconBounds.Width * 0.5);
Rectangle indexBounds = new(iconBounds.Right - indexSize, iconBounds.Bottom - indexSize, indexSize, indexSize);
Rectangle indexBounds = new Rectangle(iconBounds.Right - indexSize, iconBounds.Bottom - indexSize, indexSize, indexSize);
SizeF textSize = graphics.MeasureString(indexString, Font);
GraphicsState state = graphics.Save();
var textSize = graphics.MeasureString(indexString, font);
var 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);
}
}
@@ -254,7 +255,7 @@ namespace WorkspacesEditor.Utils
for (int iconCounter = 0; iconCounter < appsCount; iconCounter++)
{
Application app = apps.ElementAt(iconCounter);
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);
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);
try
{
@@ -263,13 +264,13 @@ namespace WorkspacesEditor.Utils
{
string indexString = app.RepeatIndex.ToString(CultureInfo.InvariantCulture);
int indexSize = (int)(iconBounds.Width * 0.5);
Rectangle indexBounds = new(iconBounds.Right - indexSize, iconBounds.Bottom - indexSize, indexSize, indexSize);
Rectangle indexBounds = new Rectangle(iconBounds.Right - indexSize, iconBounds.Bottom - indexSize, indexSize, indexSize);
SizeF textSize = graphics.MeasureString(indexString, Font);
GraphicsState state = graphics.Save();
var textSize = graphics.MeasureString(indexString, font);
var 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);
}
}
@@ -288,14 +289,14 @@ namespace WorkspacesEditor.Utils
return null;
}
Bitmap previewBitmap = new(32 * appsCount, 24);
Bitmap previewBitmap = new Bitmap(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 (Application app in project.Applications)
foreach (var app in project.Applications)
{
try
{
@@ -310,18 +311,20 @@ namespace WorkspacesEditor.Utils
}
}
using MemoryStream memory = new();
previewBitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
using (var memory = new MemoryStream())
{
previewBitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
BitmapImage bitmapImage = new();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.Freeze();
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
return bitmapImage;
}
}
private static GraphicsPath RoundedRect(Rectangle bounds)
@@ -330,9 +333,9 @@ namespace WorkspacesEditor.Utils
int radius = (int)(minorSize / 8);
int diameter = radius * 2;
Size size = new(diameter, diameter);
Rectangle arc = new(bounds.Location, size);
GraphicsPath path = new();
Size size = new Size(diameter, diameter);
Rectangle arc = new Rectangle(bounds.Location, size);
GraphicsPath path = new GraphicsPath();
if (radius == 0)
{

View File

@@ -26,16 +26,18 @@ namespace WorkspacesEditor.Utils
{
if (_fileSystem.File.Exists(fileName))
{
int attempts = 0;
var attempts = 0;
while (attempts < 10)
{
try
{
using FileSystemStream inputStream = _fileSystem.File.Open(fileName, FileMode.Open);
using StreamReader reader = new(inputStream);
string data = reader.ReadToEnd();
inputStream.Close();
return data;
using (FileSystemStream inputStream = _fileSystem.File.Open(fileName, FileMode.Open))
using (StreamReader reader = new StreamReader(inputStream))
{
string data = reader.ReadToEnd();
inputStream.Close();
return data;
}
}
catch (Exception)
{

View File

@@ -26,7 +26,7 @@ namespace WorkspacesEditor.Utils
private Screen[] GetDpiUnawareScreenBounds()
{
Thread dpiUnawareThread = new(new ThreadStart(SaveDpiUnawareScreens));
Thread dpiUnawareThread = new Thread(new ThreadStart(SaveDpiUnawareScreens));
dpiUnawareThread.Start();
dpiUnawareThread.Join();
@@ -35,15 +35,15 @@ namespace WorkspacesEditor.Utils
public static Screen[] GetDpiUnawareScreens()
{
MonitorHelper monitorHelper = new();
MonitorHelper monitorHelper = new MonitorHelper();
return monitorHelper.GetDpiUnawareScreenBounds();
}
internal static double GetScreenDpiFromScreen(Screen screen)
{
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 _);
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);
return dpiX / 96.0;
}
}

View File

@@ -4,12 +4,19 @@
namespace WorkspacesEditor.Utils
{
public readonly struct ParsingResult(bool result, string message = "", string data = "")
public struct ParsingResult
{
public bool Result { get; } = result;
public bool Result { get; }
public string Message { get; } = message;
public string Message { get; }
public string MalformedData { get; } = data;
public string MalformedData { get; }
public ParsingResult(bool result, string message = "", string data = "")
{
Result = result;
Message = message;
MalformedData = data;
}
}
}

View File

@@ -9,13 +9,13 @@ namespace WorkspacesEditor.Utils
public class Settings
{
private const string WorkspacesModuleName = "Workspaces";
private static readonly SettingsUtils _settingsUtils = new();
private static SettingsUtils _settingsUtils = new SettingsUtils();
public static WorkspacesSettings ReadSettings()
{
if (!_settingsUtils.SettingsExists(WorkspacesModuleName))
{
WorkspacesSettings defaultWorkspacesSettings = new();
var defaultWorkspacesSettings = new WorkspacesSettings();
defaultWorkspacesSettings.Save(_settingsUtils);
return defaultWorkspacesSettings;
}

View File

@@ -11,9 +11,12 @@ namespace WorkspacesEditor.Utils
public static string UpperCamelCaseToDashCase(this string str)
{
// If it's single letter variable, leave it as it is
return str.Length == 1
? str
: string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "-" + x.ToString() : x.ToString())).ToLowerInvariant();
if (str.Length == 1)
{
return str;
}
return string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "-" + x.ToString() : x.ToString())).ToLowerInvariant();
}
}
}

View File

@@ -24,7 +24,7 @@ namespace WorkspacesEditor.Utils
{
try
{
WorkspacesData parser = new();
WorkspacesData parser = new WorkspacesData();
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 parser = new ProjectData();
if (!File.Exists(TempProjectData.File))
{
Logger.LogWarning($"ParseProject method. Workspaces storage file not found: {TempProjectData.File}");
return null;
}
Project project = new(parser.Read(TempProjectData.File));
Project project = new Project(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.WorkspacesListWrapper workspacesWrapper = new() { };
workspacesWrapper.Workspaces = [];
WorkspacesData serializer = new WorkspacesData();
WorkspacesData.WorkspacesListWrapper workspacesWrapper = new WorkspacesData.WorkspacesListWrapper { };
workspacesWrapper.Workspaces = new List<ProjectData.ProjectWrapper>();
foreach (Project project in workspaces)
{
ProjectData.ProjectWrapper wrapper = new()
ProjectData.ProjectWrapper wrapper = new ProjectData.ProjectWrapper
{
Id = project.Id,
Name = project.Name,
@@ -89,11 +89,11 @@ namespace WorkspacesEditor.Utils
IsShortcutNeeded = project.IsShortcutNeeded,
MoveExistingWindows = project.MoveExistingWindows,
LastLaunchedTime = project.LastLaunchedTime,
Applications = [],
MonitorConfiguration = [],
Applications = new List<ProjectData.ApplicationWrapper> { },
MonitorConfiguration = new List<ProjectData.MonitorConfigurationWrapper> { },
};
foreach (Application app in project.Applications.Where(x => x.IsIncluded))
foreach (var app in project.Applications.Where(x => x.IsIncluded))
{
wrapper.Applications.Add(new ProjectData.ApplicationWrapper
{
@@ -120,7 +120,7 @@ namespace WorkspacesEditor.Utils
});
}
foreach (MonitorSetup monitor in project.Monitors)
foreach (var monitor in project.Monitors)
{
wrapper.MonitorConfiguration.Add(new ProjectData.MonitorConfigurationWrapper
{
@@ -150,7 +150,7 @@ namespace WorkspacesEditor.Utils
try
{
IOUtils ioUtils = new();
IOUtils ioUtils = new IOUtils();
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 (ProjectData.ProjectWrapper project in workspaces.Workspaces)
foreach (var 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 = [];
mainViewModel.Workspaces = new System.Collections.ObjectModel.ObservableCollection<Project> { };
return AddWorkspaces(mainViewModel, workspaces);
}
internal void SerializeTempProject(Project project)
{
SerializeWorkspaces([project], true);
SerializeWorkspaces(new List<Project>() { project }, true);
}
}
}

View File

@@ -156,33 +156,24 @@
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"
@@ -276,12 +267,14 @@
<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,10,0,20"
Margin="0,20,0,20"
VerticalAlignment="Center"
Background="Transparent"
Click="CancelButtonClicked">
@@ -307,8 +300,27 @@
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="1"
Grid.Row="2"
HorizontalAlignment="Stretch"
Background="{DynamicResource MonitorViewBackgroundBrush}"
CornerRadius="5">
@@ -349,47 +361,8 @@
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="3"
Grid.Row="4"
Margin="0,10,0,0"
PreviewMouseWheel="ScrollViewer_PreviewMouseWheel"
VerticalScrollBarVisibility="Auto">
@@ -404,31 +377,52 @@
</StackPanel>
</ScrollViewer>
<StackPanel
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}" />
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}" />
</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>

View File

@@ -185,6 +185,20 @@ 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;

View File

@@ -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());
};
}
}
}

View File

@@ -31,9 +31,6 @@ 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()
@@ -217,7 +214,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;
@@ -328,7 +325,7 @@ namespace Utils
}
}
}
return AppData{
.name = std::filesystem::path(appPath).stem(),
.installPath = appPath
@@ -338,7 +335,7 @@ namespace Utils
std::optional<AppData> GetApp(HWND window, const AppList& apps)
{
std::wstring processPath = get_process_path(window);
DWORD pid{};
GetWindowThreadProcessId(window, &pid);
@@ -381,15 +378,5 @@ namespace Utils
return updated;
}
bool AppData::IsEdge() const
{
return installPath.ends_with(NonLocalizable::EdgeFilename);
}
bool AppData::IsChrome() const
{
return installPath.ends_with(NonLocalizable::ChromeFilename);
}
}
}

View File

@@ -12,11 +12,7 @@ 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>;

View File

@@ -1,29 +0,0 @@
#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;
};
}

View File

@@ -1,5 +1,6 @@
#include "pch.h"
#include "WorkspacesData.h"
#include <common/SettingsAPI/settings_helpers.h>
#include <workspaces-common/GuidUtils.h>
@@ -156,7 +157,6 @@ 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))
{

View File

@@ -38,7 +38,6 @@
<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" />
@@ -52,7 +51,6 @@
<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" />
@@ -72,7 +70,6 @@
<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>
@@ -80,6 +77,5 @@
</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>

View File

@@ -41,9 +41,6 @@
<ClInclude Include="LaunchingStatus.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="PwaHelper.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
@@ -70,9 +67,6 @@
<ClCompile Include="LaunchingStatus.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="PwaHelper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

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

View File

@@ -1,6 +1,5 @@
#include "pch.h"
#include "PwaHelper.h"
#include "AppUtils.h"
#include <ShlObj.h>
#include <tlhelp32.h>
#include <winternl.h>
@@ -12,7 +11,7 @@
#include <wil\com.h>
#pragma comment(lib, "ntdll.lib")
namespace Utils
namespace SnapshotUtils
{
namespace NonLocalizable
{
@@ -23,7 +22,6 @@ namespace Utils
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);
@@ -50,10 +48,9 @@ namespace Utils
virtual HRESULT STDMETHODCALLTYPE GetAppIDForProcess(DWORD dwProcessId, WCHAR** pszAppId, void* pUnknown1, void* pUnknown2, void* pUnknown3) = 0;
};
std::optional<std::wstring> PwaHelper::GetAppId_7(HWND hWnd) const
BOOL GetAppId_7(HWND hWnd, std::wstring* result)
{
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()));
@@ -63,19 +60,19 @@ namespace Utils
hr = appResolver->GetAppIDForWindow(hWnd, &pszAppId, NULL, NULL, NULL);
if (SUCCEEDED(hr))
{
result = std::wstring(pszAppId.get());
*result = std::wstring(pszAppId.get());
}
appResolver->Release();
}
return result;
return SUCCEEDED(hr);
}
std::optional<std::wstring> PwaHelper::GetAppId_8(HWND hWnd) const
BOOL GetAppId_8(HWND hWnd, std::wstring* result)
{
HRESULT hr;
std::optional<std::wstring> result = std::nullopt;
*result = L"";
wil::com_ptr<IAppResolver_8> appResolver;
hr = CoCreateInstance(CLSID_StartMenuCacheAndAppResolver, NULL, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, IID_IAppResolver_8, reinterpret_cast<void**>(appResolver.put()));
@@ -85,29 +82,29 @@ namespace Utils
hr = appResolver->GetAppIDForWindow(hWnd, &pszAppId, NULL, NULL, NULL);
if (SUCCEEDED(hr))
{
result = std::wstring(pszAppId.get());
*result = std::wstring(pszAppId.get());
}
appResolver->Release();
}
return result;
return SUCCEEDED(hr);
}
std::wstring PwaHelper::GetAppId(HWND hWnd) const
BOOL PwaHelper::GetAppId(HWND hWnd, std::wstring* result)
{
std::optional<std::wstring> result = GetAppId_8(hWnd);
if (result == std::nullopt)
HRESULT hr = GetAppId_8(hWnd, result);
if (!SUCCEEDED(hr))
{
result = GetAppId_7(hWnd);
hr = GetAppId_7(hWnd, result);
}
return result.has_value() ? result.value() : L"";
return SUCCEEDED(hr);
}
std::optional<std::wstring> PwaHelper::GetProcessId_7(DWORD dwProcessId) const
BOOL GetProcessId_7(DWORD dwProcessId, std::wstring* result)
{
HRESULT hr;
std::optional<std::wstring> result = std::nullopt;
*result = L"";
wil::com_ptr<IAppResolver_7> appResolver;
hr = CoCreateInstance(CLSID_StartMenuCacheAndAppResolver, NULL, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, IID_IAppResolver_7, reinterpret_cast<void**>(appResolver.put()));
@@ -117,19 +114,19 @@ namespace Utils
hr = appResolver->GetAppIDForProcess(dwProcessId, &pszAppId, NULL, NULL, NULL);
if (SUCCEEDED(hr))
{
result = std::wstring(pszAppId.get());
*result = std::wstring(pszAppId.get());
}
appResolver->Release();
}
return result;
return SUCCEEDED(hr);
}
std::optional<std::wstring> PwaHelper::GetProcessId_8(DWORD dwProcessId) const
BOOL GetProcessId_8(DWORD dwProcessId, std::wstring* result)
{
HRESULT hr;
std::optional<std::wstring> result = std::nullopt;
*result = L"";
wil::com_ptr<IAppResolver_8> appResolver;
hr = CoCreateInstance(CLSID_StartMenuCacheAndAppResolver, NULL, CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER, IID_IAppResolver_8, reinterpret_cast<void**>(appResolver.put()));
@@ -139,23 +136,23 @@ namespace Utils
hr = appResolver->GetAppIDForProcess(dwProcessId, &pszAppId, NULL, NULL, NULL);
if (SUCCEEDED(hr))
{
result = std::wstring(pszAppId.get());
*result = std::wstring(pszAppId.get());
}
appResolver->Release();
}
return result;
return SUCCEEDED(hr);
}
std::wstring PwaHelper::GetProcessId(DWORD dwProcessId) const
BOOL GetProcessId(DWORD dwProcessId, std::wstring* result)
{
std::optional<std::wstring> result = GetProcessId_8(dwProcessId);
if (result == std::nullopt)
HRESULT hr = GetProcessId_8(dwProcessId, result);
if (!SUCCEEDED(hr))
{
result = GetProcessId_7(dwProcessId);
hr = GetProcessId_7(dwProcessId, result);
}
return result.has_value() ? result.value() : L"";
return SUCCEEDED(hr);
}
std::wstring GetProcCommandLine(DWORD pid)
@@ -247,7 +244,7 @@ namespace Utils
void PwaHelper::InitAumidToAppId()
{
if (m_pwaAumidToAppId.size() > 0)
if (pwaAumidToAppId.size() > 0)
{
return;
}
@@ -257,7 +254,7 @@ namespace Utils
for (const auto subProcessID : pwaHelperProcessIds)
{
std::wstring aumidID;
aumidID = GetProcessId(subProcessID);
GetProcessId(subProcessID, &aumidID);
std::wstring commandLineArg = GetProcCommandLine(subProcessID);
auto appIdIndexStart = commandLineArg.find(NonLocalizable::EdgeAppIdIdentifier);
if (appIdIndexStart != std::wstring::npos)
@@ -270,7 +267,7 @@ namespace Utils
}
}
std::wstring appId{ commandLineArg };
m_pwaAumidToAppId.insert(std::map<std::wstring, std::wstring>::value_type(aumidID, appId));
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;
@@ -296,7 +293,7 @@ namespace Utils
const std::filesystem::path filenameString = filename.path().filename();
if (filenameString.extension().wstring() == L".ico")
{
m_pwaAppIdsToAppNames.insert(std::map<std::wstring, std::wstring>::value_type(appId, filenameString.stem().wstring()));
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);
}
}
@@ -310,39 +307,41 @@ namespace Utils
}
}
std::optional<std::wstring> PwaHelper::GetPwaAppId(const std::wstring& windowAumid) const
BOOL PwaHelper::GetPwaAppId(std::wstring windowAumid, std::wstring* result)
{
const auto pwaIndex = m_pwaAumidToAppId.find(windowAumid);
if (pwaIndex != m_pwaAumidToAppId.end())
const auto pwaIndex = pwaAumidToAppId.find(windowAumid);
if (pwaIndex != pwaAumidToAppId.end())
{
return pwaIndex->second;
*result = pwaIndex->second;
return true;
}
return std::nullopt;
;
return false;
}
std::wstring PwaHelper::SearchPwaName(const std::wstring& pwaAppId, const std::wstring& windowAumid) const
BOOL PwaHelper::SearchPwaName(std::wstring pwaAppId, std::wstring windowAumid, std::wstring* pwaName)
{
const auto index = m_pwaAppIdsToAppNames.find(pwaAppId);
if (index != m_pwaAppIdsToAppNames.end())
const auto index = pwaAppIdsToAppNames.find(pwaAppId);
if (index != pwaAppIdsToAppNames.end())
{
return index->second;
*pwaName = index->second;
return true;
}
std::wstring nameFromAumid{ windowAumid };
const std::size_t delimiterPos = nameFromAumid.find(L"-");
if (delimiterPos != std::string::npos)
{
return nameFromAumid.substr(0, delimiterPos);
nameFromAumid = nameFromAumid.substr(0, delimiterPos);
}
return nameFromAumid;
*pwaName = nameFromAumid;
return false;
}
void PwaHelper::InitChromeAppIds()
{
if (m_chromeAppIds.size() > 0)
if (chromeAppIds.size() > 0)
{
return;
}
@@ -361,7 +360,7 @@ namespace Utils
if (directoryName.wstring().find(NonLocalizable::ChromeDirPrefix) == 0)
{
const std::wstring appId = directoryName.wstring().substr(NonLocalizable::ChromeDirPrefix.size());
m_chromeAppIds.push_back(appId);
chromeAppIds.push_back(appId);
for (const auto& filename : std::filesystem::directory_iterator(directory))
{
if (!filename.is_directory())
@@ -369,7 +368,7 @@ namespace Utils
const std::filesystem::path filenameString = filename.path().filename();
if (filenameString.extension().wstring() == L".ico")
{
m_pwaAppIdsToAppNames.insert(std::map<std::wstring, std::wstring>::value_type(appId, filenameString.stem().wstring()));
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);
}
}
@@ -381,73 +380,30 @@ namespace Utils
}
}
std::optional<std::wstring> PwaHelper::SearchPwaAppId(const std::wstring& windowAumid) const
BOOL PwaHelper::SearchPwaAppId(std::wstring windowAumid, std::wstring* pwaAppId)
{
const auto appIdIndexStart = windowAumid.find(NonLocalizable::ChromeAppIdIdentifier);
if (appIdIndexStart != std::wstring::npos)
{
std::wstring windowAumidSub = windowAumid.substr(appIdIndexStart + NonLocalizable::ChromeAppIdIdentifier.size());
const auto appIdIndexEnd = windowAumidSub.find(L" ");
windowAumid = windowAumid.substr(appIdIndexStart + NonLocalizable::ChromeAppIdIdentifier.size());
const auto appIdIndexEnd = windowAumid.find(L" ");
if (appIdIndexEnd != std::wstring::npos)
{
windowAumidSub = windowAumidSub.substr(0, appIdIndexEnd);
windowAumid = windowAumid.substr(0, appIdIndexEnd);
}
const std::wstring windowAumidBegin = windowAumidSub.substr(0, 10);
for (const auto chromeAppId : m_chromeAppIds)
const std::wstring windowAumidBegin = windowAumid.substr(0, 10);
for (const auto chromeAppId : chromeAppIds)
{
if (chromeAppId.find(windowAumidBegin) == 0)
{
return chromeAppId;
*pwaAppId = chromeAppId;
return true;
}
}
}
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"";
*pwaAppId = L"";
return false;
}
}

View File

@@ -0,0 +1,20 @@
#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;
};
}

View File

@@ -9,7 +9,7 @@
#include <workspaces-common/WindowFilter.h>
#include <WorkspacesLib/AppUtils.h>
#include <WorkspacesLib/PwaHelper.h>
#include <PwaHelper.h>
#pragma comment(lib, "ntdll.lib")
@@ -18,6 +18,8 @@ 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)
@@ -38,9 +40,19 @@ 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)
{
Utils::PwaHelper pwaHelper{};
PwaHelper pwaHelper{};
std::vector<WorkspacesData::WorkspacesProject::Application> apps{};
auto installedApps = Utils::Apps::GetAppsList();
@@ -116,7 +128,47 @@ namespace SnapshotUtils
continue;
}
pwaHelper.UpdatePwaApp(&data.value(), window);
std::wstring pwaAppId = L"";
std::wstring finalName = data.value().name;
std::wstring pwaName = L"";
if (IsEdge(data.value()))
{
pwaHelper.InitAumidToAppId();
std::wstring windowAumid;
pwaHelper.GetAppId(window, &windowAumid);
Logger::info(L"Found an edge window with aumid {}", windowAumid);
if (pwaHelper.GetPwaAppId(windowAumid, &pwaAppId))
{
Logger::info(L"The found edge window is a PWA app with appId {}", pwaAppId);
if (pwaHelper.SearchPwaName(pwaAppId, windowAumid ,& pwaName))
{
Logger::info(L"The found edge window is a PWA app with name {}", finalName);
}
finalName = pwaName + L" (" + finalName + L")";
}
else
{
Logger::info(L"The found edge window does not contain a PWA app", pwaAppId);
}
}
else if (IsChrome(data.value()))
{
pwaHelper.InitChromeAppIds();
std::wstring windowAumid;
pwaHelper.GetAppId(window, &windowAumid);
Logger::info(L"Found a chrome window with aumid {}", windowAumid);
if (pwaHelper.SearchPwaAppId(windowAumid, &pwaAppId))
{
if (pwaHelper.SearchPwaName(pwaAppId, windowAumid, &pwaName))
{
finalName = pwaName + L" (" + finalName + L")";
}
}
}
bool isMinimized = WindowUtils::IsMinimized(window);
unsigned int monitorNumber = getMonitorNumberFromWindowHandle(window);
@@ -132,12 +184,12 @@ namespace SnapshotUtils
}
WorkspacesData::WorkspacesProject::Application app{
.name = data.value().name,
.name = finalName,
.title = title,
.path = data.value().installPath,
.packageFullName = data.value().packageFullName,
.appUserModelId = data.value().appUserModelId,
.pwaAppId = data.value().pwaAppId,
.pwaAppId = pwaAppId,
.commandLineArgs = L"",
.isElevated = IsProcessElevated(pid),
.canLaunchElevated = data.value().canLaunchElevated,

View File

@@ -130,10 +130,12 @@
<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>

View File

@@ -24,6 +24,9 @@
<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">
@@ -35,6 +38,9 @@
<ClCompile Include="SnapshotUtils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="PwaHelper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@@ -12,18 +12,9 @@
#include <workspaces-common/WindowUtils.h>
#include <WindowProperties/WorkspacesWindowPropertyUtils.h>
#include <WorkspacesLib/PwaHelper.h>
namespace PlacementHelper
namespace FancyZones
{
// 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) };
@@ -61,7 +52,14 @@ namespace PlacementHelper
}
else
{
placement.showCmd = SW_RESTORE;
if ((placement.showCmd != SW_SHOWMINIMIZED) &&
(placement.showCmd != SW_MINIMIZE))
{
if (placement.showCmd == SW_SHOWMAXIMIZED)
placement.flags &= ~WPF_RESTORETOMAXIMIZED;
placement.showCmd = SW_RESTORE;
}
ScreenToWorkAreaCoords(window, monitor, rect);
placement.rcNormalPosition = rect;
@@ -93,191 +91,18 @@ namespace PlacementHelper
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;
@@ -313,17 +138,27 @@ 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);
@@ -359,11 +194,12 @@ 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())
{
@@ -422,7 +258,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 (PlacementHelper::SizeWindowToRect(window, currentMonitor, launchMinimized, launchMaximized, rect))
if (FancyZones::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);

View File

@@ -5,15 +5,8 @@
#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:
@@ -28,9 +21,7 @@ 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);

View File

@@ -1,8 +1,6 @@
#pragma warning disable IDE0073
// Copyright (c) Brice Lambson
// 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.

View File

@@ -1,8 +1,6 @@
#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;

View File

@@ -1,8 +1,6 @@
#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;

View File

@@ -1,8 +1,6 @@
#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;

View File

@@ -1,8 +1,6 @@
#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;

View File

@@ -1,8 +1,6 @@
#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;

View File

@@ -1,8 +1,6 @@
#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;

View File

@@ -1,8 +1,6 @@
#pragma warning disable IDE0073
// Copyright (c) Brice Lambson
// 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;

View File

@@ -1,8 +1,6 @@
#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;

View File

@@ -1,8 +1,6 @@
#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;

View File

@@ -1,8 +1,6 @@
#pragma warning disable IDE0073
// Copyright (c) Brice Lambson
// 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;

View File

@@ -1,8 +1,6 @@
#pragma warning disable IDE0073
// Copyright (c) Brice Lambson
// 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
{

View File

@@ -1,8 +1,6 @@
#pragma warning disable IDE0073
// Copyright (c) Brice Lambson
// 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
{

View File

@@ -1,8 +1,6 @@
#pragma warning disable IDE0073
// Copyright (c) Brice Lambson
// 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
{

View File

@@ -1,8 +1,6 @@
#pragma warning disable IDE0073
// Copyright (c) Brice Lambson
// 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;

View File

@@ -1,8 +1,6 @@
#pragma warning disable IDE0073
// Copyright (c) Brice Lambson
// 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;

View File

@@ -1,8 +1,6 @@
#pragma warning disable IDE0073
// Copyright (c) Brice Lambson
// 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
{

View File

@@ -1,9 +1,7 @@
#pragma warning disable IDE0073
// Copyright (c) Brice Lambson
// 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
{

View File

@@ -1,8 +1,6 @@
#pragma warning disable IDE0073
// Copyright (c) Brice Lambson
// 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;

View File

@@ -1,9 +1,7 @@
#pragma warning disable IDE0073
// Copyright (c) Brice Lambson
// 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;

View File

@@ -1,9 +1,7 @@
#pragma warning disable IDE0073
// Copyright (c) Brice Lambson
// 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
{

View File

@@ -1,8 +1,6 @@
#pragma warning disable IDE0073
// Copyright (c) Brice Lambson
// 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;

View File

@@ -1,8 +1,6 @@
#pragma warning disable IDE0073
// Copyright (c) Brice Lambson
// 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;

View File

@@ -1,8 +1,6 @@
#pragma warning disable IDE0073
// Copyright (c) Brice Lambson
// 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;

View File

@@ -1,8 +1,6 @@
#pragma warning disable IDE0073
// Copyright (c) Brice Lambson
// 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;

View File

@@ -1,8 +1,6 @@
#pragma warning disable IDE0073
// Copyright (c) Brice Lambson
// 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;

View File

@@ -1,8 +1,6 @@
#pragma warning disable IDE0073
// Copyright (c) Brice Lambson
// 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;

View File

@@ -1,8 +1,6 @@
#pragma warning disable IDE0073
// Copyright (c) Brice Lambson
// 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;

View File

@@ -1,8 +1,6 @@
#pragma warning disable IDE0073
// Copyright (c) Brice Lambson
// 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
{

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