Compare commits

..

27 Commits

Author SHA1 Message Date
Jaime Bernardo
8b605fb633 [ci]Use 1ES Pipeline Templates release from 2024-06-21 2024-07-11 15:06:14 +01:00
Jaime Bernardo
d64642f29c [PTRun]Don't apply transparency fix after theme change on Windows 10 (#33783) 2024-07-11 14:08:48 +01:00
Jaime Bernardo
57b06fa431 [Installer]Create DSC module in right path for user installs (#33782) 2024-07-11 13:49:52 +01:00
Clint Rutkas
7b37eba0f9 [PTRun]Don't apply title bar fix to Windows build 22000 to avoid crash (#33687) 2024-07-11 11:34:17 +01:00
Clint Rutkas
24bed6f0bb CI is not liking the FancyZone UI tests, disabling to help unblock PRs (#33746)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

Disabling a UI test as right now it is blocking CI

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [ ] **Closes:** #xxx
- [ ] **Communication:** I've discussed this with core contributors
already. If work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [ ] **Localization:** All end user facing strings can be localized
- [ ] **Dev docs:** Added/updated
- [ ] **New binaries:** Added on the required places
- [ ] [JSON for
signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json)
for new binaries
- [ ] [WXS for
installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs)
for new binaries and localization folder
- [ ] [YML for CI
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml)
for new test projects
- [ ] [YML for signed
pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml)
- [ ] **Documentation updated:** If checked, please file a pull request
on [our docs
repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys)
and link it here: #xxx

<!-- Provide a more detailed description of the PR, other things fixed
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2024-07-09 17:44:55 -07:00
Jaime Bernardo
74fbc519ab 0.82 changelog (#33500)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

Readme update for the 0.82 release.
This will be copy / pasted for release notes.

---------

Co-authored-by: Clint Rutkas <clint@rutkas.com>
Co-authored-by: Jay <65828559+Jay-o-Way@users.noreply.github.com>
2024-07-02 08:37:16 -07:00
Craig Loewen
3b7adbe6ac [AdvancedPaste]Improved telemetry (#33520)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

This PR improves advanced paste telemetry. Here's what's changed

- Added `AdvancedPasteClipboardItemClicked` event
- Changed `CustomFormatEvent` to only fire on successful completion and
include the number of tokens used, and the model name

Here are the goals of adding this telemtry:

- `AdvancedPasteClipboardItemClicked` helps us estimate the total number
of user who are using the clipboard history feature, which helps us
prioritize future investments and improvements in PowerToys. (This is
just regular feature usage data).
- `CustomFormatEvent` now includes number of tokens used, and the model
name. We are considering using alternative models to power Advanced
Paste (as we've heard feedback about this), and these different models
could have different token lengths. Understanding the average usage will
help us make good decisions that will benefit the most users. As well,
the model name is hard coded right now, but in the future if we do add
different AI models for completion then this will help us compare their
performance.

<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [X] Communication: I've discussed this with core contributors already.
If work hasn't been agreed, this work might be rejected


<!-- Provide a more detailed description of the PR, other things fixed
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

The details above are detailed enough since this change is super small. 

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed

Ensured that PowerToys successfully built (Need help verifying the
events fire off correctly).
2024-06-27 16:42:15 +01:00
Jaime Bernardo
8b8c75b9a5 [PTRun]Disable titlebar accent workaround on Windows 10 (#33505)
After https://github.com/microsoft/PowerToys/pull/33458 , that fix
crashes on Windows 10, where the caption color attribute is not
supported.

This PR disables the fix on Windows 10, since it's not even needed there
actually.
2024-06-25 20:57:05 +01:00
Dustin L. Howett
5c257fb3db Rewrite MSStore submission pipeline to use msstore-cli and Cert Auth (#33430) 2024-06-24 09:45:59 -05:00
Davide Giacometti
6e141f89c9 [AdvPaste]Add option to hide the window when it loses focus (#33239) 2024-06-24 15:03:46 +01:00
Masaru Iritani
62c7b0a66d [KBM][CQ]Replace LPINPUT with std::vector<INPUT> (#32052) 2024-06-21 22:16:41 +01:00
Jaime Bernardo
9509d7c1cc [PTRun]Bring back acrylic and proper fix to title bar accent showing (#33458)
* Revert "[PTRun]Fix accent on title bar bleed into UI (#33046)"

This reverts commit 8bb5a33572.

* Revert "[PTRun]Use Mica backdrop to fix crashes with WPFUI (#32118)"

This reverts commit b9da1e6abf.

* Fix DWMAttributes in Wox Plugin Native Methods

* Fix titlebar accent showing

* Fix number on wrong enum
2024-06-21 10:30:13 +01:00
Jaime Bernardo
6043898ee5 [build][settings]Disable experimentation code (#33452)
* [build][settings]Disable experimentation code

* Update src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeShellPage.xaml.cs

Co-authored-by: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com>

* Update src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeShellPage.xaml.cs

Co-authored-by: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com>

* Update src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeShellPage.xaml.cs

---------

Co-authored-by: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com>
2024-06-20 17:12:32 +01:00
Jaime Bernardo
136b239f96 [ColorPicker]Fix picker opaque corners (#33457) 2024-06-20 16:27:52 +01:00
Jaakko Hirvioja
c148b51698 [PowerRename]Add random string values to file names (#32836)
* Add randomizer cheat sheet texts to UI tooltip

* Add randomizer icon (shuffle) + hint to main window

* Add randomizer logic + helpers, regex parsing

* Fix: remove unnecessary throw

* Fix: remove todo comment

* Fix: iffing logic

* Fix: add offset to randomizer onchange

* Update: guid generating to single function, handle bracket removing there

* Update: toggle off enum feat when random values are selected

* Update: main window UI tooltip texts to be more descriptive

* Update: remove unnecessary sstream include

* Fix: return empty string if chars has no value to avoid memory access violation

* Add unit tests

* Add PowerRename random string generating keywords

* Update: generating value names to be in line with POSIX conventions

* Allow to used with Enumerate at the same time

* Fix spellcheck

* Fix tests to take into account we no longer eat up empty expressions
with randomizer

---------

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
2024-06-20 16:26:31 +01:00
Jvr
1ae8327a43 [Deps]Update Unitsnet to 5.50.0 (#32572)
* [DEPS] Update Unitsnet to 5.50.0

* [DEPS] Update Unitsnet to 5.50.0

* fix bug 1
2024-06-20 10:27:29 +01:00
Jvr
aa977f7579 [Deps]Update Xamlstyler.console to 3.2404.2 (#32567)
* Update Xamlstyler.console to 3.2404.4

* Update dotnet-tools.json
2024-06-20 10:15:55 +01:00
Stefan Markovic
dcbff83d8c [AdvPaste]Clear image sources to fix memory leak (#33438)
* [AdvancedPaste] Clear image sources to fix memory leak
2024-06-19 16:53:39 +01:00
Ani
5c631bd2c7 [Peek]Fixed crash caused by COM object double release in runner (#33427) 2024-06-19 16:01:17 +01:00
Dustin L. Howett
c252d87573 build: make the agent pool selection more robust (#33429)
We are no longer exclusively using one organization named "ms" and one organization not named "ms".

This change flips the sense of the organization comparison so the `OSS` agents can be used for every collection except very specifically the Microsoft one. I also switched to using its ID.
2024-06-18 11:33:01 -07:00
octastylos-pseudodipteros
92483aee1a [QuickAccent]Add feminine and masculine ordinal indicator to Portuguese (#33420) 2024-06-17 22:52:24 +01:00
Dominik Downarowicz
2eec4c5a98 [Monaco][CQ]Remove double handling of the sticky scroll option (#33384) 2024-06-17 21:44:06 +01:00
Heiko
109f0b210a [AdvPaste]Add support for converting from ini to json (#33201)
* add ini parser

* improvements

* fix spelling

* fix regex and add debug log info

* fix if condition

* fix comment

* skip comments

* more improvements

* update comment

* better error handling

* fix spelling
2024-06-17 12:40:19 +01:00
Ani
f019163083 [Settings]Ensure PTRun plugin icon paths are well formed / add unhandled exception handler (#33374)
* Fixed production crash with bad icon paths in Launcher plugins

* Bumped MSBuildCacheCacheUniverse
2024-06-16 16:58:25 +01:00
Ani
deb6234d72 [PreviewHandlers]Check for Disposed state when updating window bounds (#33373)
* Fixed Previewer production crash with an ObjectDisposedException

* Bumped MSBuildCacheCacheUniverse
2024-06-16 16:32:33 +01:00
PesBandi
cdf5677eb9 [Monaco]Improve .gitignore definition (#33263)
* Update gitignore.js

* Update gitignore.js

* Add custom color for negations

* Add custom color for negations

* Regex refactoring

* Regex refactoring again

* Move customTokenColors to a separate file

* Move customTokenColors to a separate file

* Update devdocs

* Use kebab case for token names

* Update negation color

* Update index.html formatting
2024-06-14 16:09:52 +01:00
Ani
bc0811e6a1 [Peek]Support for special folders like Recycle Bin and My PC (#33310)
* Peek support for special folders

* Renamed ThisComputer->ThisPC

* Used different variable name to avoid spellcheck issues

* Made label of empty fields hidden

* Removed ThisPC special handling and last modified date of recycle bin
2024-06-14 15:40:25 +01:00
239 changed files with 2590 additions and 10820 deletions

View File

@@ -9,7 +9,7 @@
]
},
"xamlstyler.console": {
"version": "3.2206.4",
"version": "3.2404.2",
"commands": [
"xstyler"
]

View File

@@ -68,7 +68,6 @@ body:
- Peek
- PowerRename
- PowerToys Run
- Projects
- Quick Accent
- Registry Preview
- Screen ruler

View File

@@ -42,7 +42,6 @@ body:
- Peek
- PowerRename
- PowerToys Run
- Projects
- Quick Accent
- Registry Preview
- Screen ruler

View File

@@ -221,3 +221,8 @@ artanh
arsinh
arcosh
# Linux
dbus
anypass
gpg

View File

@@ -61,7 +61,6 @@ appmanifest
APPNAME
appref
appsettings
appsfolder
appwindow
appwiz
APSTUDIO
@@ -96,6 +95,7 @@ AUTOUPDATE
AValid
awakeness
AWAYMODE
azcliversion
azman
backtracer
bbwe
@@ -120,6 +120,7 @@ BLURREGION
bmi
bms
BNumber
BODGY
BOKMAL
bootstrapper
BOOTSTRAPPERINSTALLFOLDER
@@ -166,6 +167,7 @@ CENTERALIGN
ceq
certlm
certmgr
cfp
cguid
CHANGECBCHAIN
changecursor
@@ -239,7 +241,6 @@ CONTEXTMENUHANDLER
CONTROLL
CONTROLPARENT
copiedcolorrepresentation
COREWINDOW
cotaskmem
COULDNOT
countof
@@ -756,6 +757,7 @@ KEYEVENTF
KEYIMAGE
keynum
keyremaps
keyvault
KILLFOCUS
killrunner
Knownfolders
@@ -1207,14 +1209,9 @@ PRODUCTVERSION
Progman
programdata
projectname
PROJECTSEDITOR
PROJECTSLAUNCHER
PROJECTSSNAPSHOTTOOL
PROPBAG
PROPERTYKEY
propkey
propsys
PROPVARIANT
propvarutil
prvpane
psapi
@@ -1345,6 +1342,9 @@ RRF
rrr
rsop
Rsp
rstringalnum
rstringalpha
rstringdigit
Rstrtmgr
RTB
RTLREADING
@@ -1359,6 +1359,7 @@ runtimeclass
runtimeobject
runtimepack
runtimes
ruuid
rvm
rwin
rwl
@@ -1380,7 +1381,6 @@ sddl
SDKDDK
sdns
searchterm
SEARCHUI
secpol
SENDCHANGE
sendinput
@@ -1564,9 +1564,7 @@ SYSKEYUP
SYSLIB
SYSMENU
SYSTEMAPPS
systemsettings
SYSTEMTIME
SYSTEMWOW
tapp
TApplication
TApplied
@@ -1678,11 +1676,9 @@ urlmon
Usb
USEDEFAULT
USEFILEATTRIBUTES
USEPOSITION
USERDATA
Userenv
USESHOWWINDOW
USESIZE
USESTDHANDLES
USRDLL
UType

View File

@@ -5,56 +5,80 @@ on:
release:
types: [published]
permissions:
id-token: write
jobs:
microsoft_store:
name: Publish Microsoft Store
environment: store
runs-on: ubuntu-latest
steps:
- name: BODGY - Set up Gnome Keyring for future Cert Auth
run: |-
sudo apt-get install -y gnome-keyring
export $(dbus-launch --sh-syntax)
export $(echo 'anypass_just_to_unlock' | gnome-keyring-daemon --unlock)
export $(echo 'anypass_just_to_unlock' | gnome-keyring-daemon --start --components=gpg,pkcs11,secrets,ssh)
- name: Log in to Azure
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
enable-AzPSSession: true
- name: Get latest URL from public releases
id: releaseVars
run: |
release=$(curl https://api.github.com/repos/Microsoft/PowerToys/releases | jq '[.[]|select(.name | contains("Release"))][0]')
assets=$(jq -n "$release" | jq '.assets')
powerToysSetup=$(jq -n "$assets" | jq '[.[]|select(.name | contains("PowerToysUserSetup"))]')
echo ::set-output name=powerToysInstallerX64Url::$(jq -n "$powerToysSetup" | jq -r '[.[]|select(.name | contains("x64"))][0].browser_download_url')
echo ::set-output name=powerToysInstallerArm64Url::$(jq -n "$powerToysSetup" | jq -r '[.[]|select(.name | contains("arm64"))][0].browser_download_url')
echo powerToysInstallerX64Url=$(jq -n "$powerToysSetup" | jq -r '[.[]|select(.name | contains("x64"))][0].browser_download_url') >> $GITHUB_OUTPUT
echo powerToysInstallerArm64Url=$(jq -n "$powerToysSetup" | jq -r '[.[]|select(.name | contains("arm64"))][0].browser_download_url') >> $GITHUB_OUTPUT
- uses: microsoft/setup-msstore-cli@v1
- name: Fetch Store Credential
uses: azure/cli@v2
with:
azcliversion: latest
inlineScript: |-
az keyvault secret download --vault-name ${{ secrets.AZURE_KEYVAULT_NAME }} -n ${{ secrets.AZURE_AUTH_CERT_NAME }} -f cert.pfx.b64
base64 -d < cert.pfx.b64 > cert.pfx
- name: Configure Store Credentials
uses: microsoft/store-submission@v1
with:
command: configure
type: win32
seller-id: ${{ secrets.SELLER_ID }}
product-id: ${{ secrets.PRODUCT_ID }}
tenant-id: ${{ secrets.TENANT_ID }}
client-id: ${{ secrets.CLIENT_ID }}
client-secret: ${{ secrets.CLIENT_SECRET }}
run: |-
msstore reconfigure -cfp cert.pfx -c ${{ secrets.AZURE_CLIENT_ID }} -t ${{ secrets.AZURE_TENANT_ID }} -s ${{ secrets.SELLER_ID }}
- name: Update draft submission
uses: microsoft/store-submission@v1
with:
command: update
product-update: '{
"packages":[
{
"packageUrl":"${{ steps.releaseVars.outputs.powerToysInstallerX64Url }}",
"languages":["zh-hans", "zh-hant", "en", "cs", "nl", "fr", "pt", "pt-br", "de", "hu", "it", "ja", "ko", "pl", "ru", "es", "tr"],
"architectures":["X64"],
"installerParameters":"/quiet /norestart",
"isSilentInstall":true
},
{
"packageUrl":"${{ steps.releaseVars.outputs.powerToysInstallerArm64Url }}",
"languages":["zh-hans", "zh-hant", "en", "cs", "nl", "fr", "pt", "pt-br", "de", "hu", "it", "ja", "ko", "pl", "ru", "es", "tr"],
"architectures":["Arm64"],
"installerParameters":"/quiet /norestart",
"isSilentInstall":true
}
]
}'
run: |-
msstore submission update ${{ secrets.PRODUCT_ID }} '{
"packages":[
{
"packageUrl":"${{ steps.releaseVars.outputs.powerToysInstallerX64Url }}",
"languages":["zh-hans", "zh-hant", "en", "cs", "nl", "fr", "pt", "pt-br", "de", "hu", "it", "ja", "ko", "pl", "ru", "es", "tr"],
"architectures":["X64"],
"installerParameters":"/quiet /norestart",
"isSilentInstall":true
},
{
"packageUrl":"${{ steps.releaseVars.outputs.powerToysInstallerArm64Url }}",
"languages":["zh-hans", "zh-hant", "en", "cs", "nl", "fr", "pt", "pt-br", "de", "hu", "it", "ja", "ko", "pl", "ru", "es", "tr"],
"architectures":["Arm64"],
"installerParameters":"/quiet /norestart",
"isSilentInstall":true
}
]
}'
- name: Publish Submission
uses: microsoft/store-submission@v1
with:
command: publish
run: |-
msstore submission publish ${{ secrets.PRODUCT_ID }}
- name: Clean up auth certificate
if: always()
run: |-
rm -f cert.pfx cert.pfx.b64

View File

@@ -189,12 +189,6 @@
"WinUI3Apps\\PowerToys.PowerRenameContextMenu.dll",
"WinUI3Apps\\PowerRenameContextMenuPackage.msix",
"PowerToys.ProjectsSnapshotTool.exe",
"PowerToys.ProjectsLauncher.exe",
"PowerToys.ProjectsEditor.exe",
"PowerToys.ProjectsEditor.dll",
"PowerToys.ProjectsModuleInterface.dll",
"WinUI3Apps\\PowerToys.RegistryPreviewExt.dll",
"WinUI3Apps\\PowerToys.RegistryPreviewUILib.dll",
"WinUI3Apps\\PowerToys.RegistryPreview.dll",

View File

@@ -41,6 +41,6 @@ jobs:
platform: arm64
${{ if eq(variables['System.PullRequest.IsFork'], 'False') }}:
enableCaching: true
- template: ./templates/run-ui-tests-ci.yml
parameters:
platform: x64
# - template: ./templates/run-ui-tests-ci.yml
# parameters:
# platform: x64

View File

@@ -24,10 +24,10 @@ jobs:
NODE_OPTIONS: --max_old_space_size=16384
pool:
demands: ImageOverride -equals SHINE-VS17-Latest
${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
name: SHINE-OSS-L
${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
${{ if eq(variables['System.CollectionId'], 'cb55739e-4afe-46a3-970f-1b49d8ee7564') }}:
name: SHINE-INT-L
${{ else }}:
name: SHINE-OSS-L
timeoutInMinutes: 120
strategy:
maxParallel: 10

View File

@@ -3,10 +3,10 @@ jobs:
- job: Precheck
pool:
demands: ImageOverride -equals SHINE-VS17-Latest
${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
name: SHINE-OSS-L
${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
${{ if eq(variables['System.CollectionId'], 'cb55739e-4afe-46a3-970f-1b49d8ee7564') }}:
name: SHINE-INT-L
${{ else }}:
name: SHINE-OSS-L
steps:
- checkout: none

View File

@@ -9,10 +9,10 @@ jobs:
variables:
SrcPath: $(Build.Repository.LocalPath)
pool:
${{ if eq(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
name: SHINE-OSS-Testing-x64
${{ if ne(variables['System.CollectionUri'], 'https://dev.azure.com/ms/') }}:
${{ if eq(variables['System.CollectionId'], 'cb55739e-4afe-46a3-970f-1b49d8ee7564') }}:
name: SHINE-INT-Testing-x64
${{ else }}:
name: SHINE-OSS-Testing-x64
steps:
- checkout: self
fetchDepth: 1

View File

@@ -8,7 +8,7 @@ resources:
- repository: 1ESPipelineTemplates
type: git
name: 1ESPipelineTemplates/1ESPipelineTemplates
ref: refs/tags/release
ref: refs/tags/release-2024-06-21-2
parameters:
- name: buildConfigurations
@@ -76,7 +76,7 @@ extends:
NODE_OPTIONS: --max_old_space_size=16384
IsPipeline: 1 # The installer uses this to detect whether it should pick up localizations
SkipCppCodeAnalysis: 1 # Skip the code analysis to speed up release CI. It runs on PR CI, anyway
IsExperimentationLive: 1 # The build and installer use this to turn on experimentation
# IsExperimentationLive: 1 # The build and installer use this to turn on experimentation
steps:
- checkout: self
clean: true

View File

@@ -85,7 +85,7 @@
<UsePrecompiledHeaders Condition="'$(TF_BUILD)' != ''">false</UsePrecompiledHeaders>
<!-- Change this to bust the cache -->
<MSBuildCacheCacheUniverse Condition="'$(MSBuildCacheCacheUniverse)' == ''">202310210737</MSBuildCacheCacheUniverse>
<MSBuildCacheCacheUniverse Condition="'$(MSBuildCacheCacheUniverse)' == ''">202407100737</MSBuildCacheCacheUniverse>
<!--
Visual Studio telemetry reads various ApplicationInsights.config files and other files after the project is finished, likely in a detached process.

View File

@@ -86,7 +86,7 @@
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="8.0.0" />
<PackageVersion Include="System.Text.Encoding.CodePages" Version="8.0.0" />
<PackageVersion Include="UnicodeInformation" Version="2.6.0" />
<PackageVersion Include="UnitsNet" Version="4.145.0" />
<PackageVersion Include="UnitsNet" Version="5.50.0" />
<PackageVersion Include="UTF.Unknown" Version="2.5.1" />
<PackageVersion Include="Vanara.PInvoke.User32" Version="3.4.11" />
<PackageVersion Include="Vanara.PInvoke.Shell32" Version="3.4.11" />
@@ -98,4 +98,4 @@
<PackageVersion Include="Microsoft.VariantAssignment.Client" Version="2.4.17140001" />
<PackageVersion Include="Microsoft.VariantAssignment.Contract" Version="3.0.16990001" />
</ItemGroup>
</Project>
</Project>

View File

@@ -1365,7 +1365,7 @@ EXHIBIT A -Mozilla Public License.
- System.ServiceProcess.ServiceController 8.0.0
- System.Text.Encoding.CodePages 8.0.0
- UnicodeInformation 2.6.0
- UnitsNet 4.145.0
- UnitsNet 5.50.0
- UTF.Unknown 2.5.1
- Vanara.PInvoke.Shell32 3.4.11
- Vanara.PInvoke.User32 3.4.11

File diff suppressed because it is too large Load Diff

195
README.md
View File

@@ -41,19 +41,19 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
Go to the [Microsoft PowerToys GitHub releases page][github-release-link] and click on `Assets` at the bottom to show the files available in the release. Please use the appropriate PowerToys installer that matches your machine's architecture and install scope. For most, it is `x64` and per-user.
<!-- items that need to be updated release to release -->
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.82%22
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=project%3Amicrosoft%2FPowerToys%2F54
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.81.0/PowerToysUserSetup-0.81.0-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.81.0/PowerToysUserSetup-0.81.0-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.81.0/PowerToysSetup-0.81.0-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.81.0/PowerToysSetup-0.81.0-arm64.exe
[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.83%22
[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.82%22
[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.82.0/PowerToysUserSetup-0.82.0-x64.exe
[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.82.0/PowerToysUserSetup-0.82.0-arm64.exe
[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.82.0/PowerToysSetup-0.82.0-x64.exe
[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.82.0/PowerToysSetup-0.82.0-arm64.exe
| Description | Filename | sha256 hash |
|----------------|----------|-------------|
| Per user - x64 | [PowerToysUserSetup-0.81.0-x64.exe][ptUserX64] | E62B1EE81954A75355C04E7567B1C9AAD6034AA0C61AD22587F8746D0DC488C8 |
| Per user - ARM64 | [PowerToysUserSetup-0.81.0-arm64.exe][ptUserArm64] | 75330A2DB4F9EF9B548B3B58F8BF3262C8C67E680042639BBBBC87EA244F24E2 |
| Machine wide - x64 | [PowerToysSetup-0.81.0-x64.exe][ptMachineX64] | 29F151B01FE3C94D4FD75F2D6E8F09A6C0F0962385B83A5A733F6717312F639D |
| Machine wide - ARM64 | [PowerToysSetup-0.81.0-arm64.exe][ptMachineArm64] | FCE636220E1FB854771258D9558E07B7532728AD4C722A7920338DEE60DEECF7 |
| Per user - x64 | [PowerToysUserSetup-0.82.0-x64.exe][ptUserX64] | 295E2A4622C7E347D3E1BAEA6B36BECC328B566496678F1F87DE3F8A12A7F89A |
| Per user - ARM64 | [PowerToysUserSetup-0.82.0-arm64.exe][ptUserArm64] | 55D25D068C6148F0A15C7806B9F813224ABA9C461943F42BB2A8B0E22D28240C |
| Machine wide - x64 | [PowerToysSetup-0.82.0-x64.exe][ptMachineX64] | 01B59C00BB43C25BEFEF274755875717AB4DEAB57C0354AB96CF5B1DA4837C9A |
| Machine wide - ARM64 | [PowerToysSetup-0.82.0-arm64.exe][ptMachineArm64] | 1F642B50962516127793C4D3556BF4FC24B9738BAC2F362CAA3BFF8B0C3AF97F |
This is our preferred method.
@@ -99,155 +99,134 @@ For guidance on developing for PowerToys, please read the [developer docs](/doc/
Our [prioritized roadmap][roadmap] of features and utilities that the core team is focusing on.
### 0.81 - Build 2024 Update
### 0.82 - June 2024 Update
In this release, we focused on new features, stability and improvements.
In this release, we focused on stability and improvements.
**Highlights**
- New utility: Advanced Paste - This is an evolution based on feedback of the Paste As Plain Text utility to do more. It can paste as plain text, markdown, or json directly with the new UX or with a direct keystroke invoke. These are fully locally executed. In addition, it now has an AI powered option as well if you wish with the free form text box. The AI feature is 100% opt-in and requires an Open AI key. This new system will allow us to have more freedom in the future to quickly add in new features like pasting an image directly to a file or handle additional meta data types past just text.
- Thanks [@craigloewen-msft](https://github.com/craigloewen-msft) for the core functionality and [@niels9001](https://github.com/niels9001) for the UI/UX design!
- Command Not Found now uses the PowerShell Gallery release and now supports ARM64. Thanks [@carlos-zamora](https://github.com/carlos-zamora)!
- Fixed most accessibility issues opened after the latest accessibility review.
- Refactored, packaged and released the main Environment Variables Editor, Hosts File Editor and Registry Preview utilities functionality as controls to be integrated into DevHome. Thanks [@dabhattimsft](https://github.com/dabhattimsft) for validating and integrating into DevHome!
### General
- Fixed crashes on older CPUS by updating .NET to 8.0.4. (This was a hotfix for 0.80)
- New feature added to PowerRename to allow using sequences of random characters and UUIDs when renaming files. Thanks [@jhirvioja](https://github.com/jhirvioja)!
- Improvements in the Paste As JSON feature to better handle other CSV delimiters and converting from ini files. Thanks [@htcfreek](https://github.com/htcfreek)!
- Fixed UI issues that were reported after upgrading to WPF UI on Color Picker and PowerToys Run.
- Bug fixes and stability.
### Advanced Paste
- New utility: Advanced Paste - This is an evolution based on feedback of the Paste As Plain Text utility to do more. It can paste as plain text, markdown, or json directly with the new UX or with a direct keystroke invoke. These are fully locally executed. In addition, it now has an AI powered option as well if you wish with the free form text box. The AI feature is 100% opt-in and requires an Open AI key. This new system will allow us to have more freedom in the future to quickly add in new features like pasting an image directly to a file or handle additional meta data types past just text.
- Thanks [@craigloewen-msft](https://github.com/craigloewen-msft) for the core functionality and [@niels9001](https://github.com/niels9001) for the UI/UX design!
### AlwaysOnTop
- Enable border anti-aliasing. Thanks [@ewancg](https://github.com/ewancg)!
- Fixed an issue causing external applications triggering Advanced Paste. (This was a hotfix for 0.81)
- Added a GPO rule to disallow using online models in Advanced Paste. (This was a hotfix for 0.81)
- Improved CSV delimiter handling and plain text parsing for the Paste as JSON feature. Thanks [@htcfreek](https://github.com/htcfreek)!
- Added support to convert from ini in the Paste as JSON feature. Thanks [@htcfreek](https://github.com/htcfreek)!
- Fixed a memory leak caused by images not being properly cleaned out from clipboard history.
- Added an option to hide the UI when it loses focus. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Improved telemetry to get better data about token usage and if clipboard history is a popular feature. Thanks [@craigloewen-msft](https://github.com/craigloewen-msft)!
### Color Picker
- Improved accessibility by making the Settings and Copy to clipboard buttons focusable.
- Improved accessibility by supporting picking a color using the keyboard.
- Fixed the opaque background corners in the picker that were introduced after the upgrade to WPFUI.
### Command Not Found
### Developer Files Preview (Monaco)
- Upgraded the Command Not Found to use the new PowerShell Gallery release and support ARM64. Thanks [@carlos-zamora](https://github.com/carlos-zamora)!
- Improved the syntax highlight for .gitignore files. Thanks [@PesBandi](https://github.com/PesBandi)!
- Checking for the sticky scroll option in code behind was being done twice. Removed one of the checks. Thanks [@downarowiczd](https://github.com/downarowiczd)!
### Environment Variables Editor
- Refactored, packaged and released the main Environment Variables Editor functionality as a control to be integrated into DevHome. Thanks [@dabhattimsft](https://github.com/dabhattimsft) for validating and integrating into DevHome!
### FancyZones
- Fixed window wrap around behavior when overriding Windows key and arrow shortcuts on single monitor scenarios. Thanks [@DanRosenberry](https://github.com/DanRosenberry)!
- Improved accessibility of the editor by listing the keyboard shortcuts in the Canvas Editor.
- Added clarity to the UI section tooltips. Thanks [@anson-poon](https://github.com/anson-poon)!
### File Explorer add-ons
- Updated Monaco to 0.47 and added the new sticky scroll setting for DevFiles viewer. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)!
- Added the new font size setting for DevFiles viewer. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)!
- Added support for .srt (subtitle) file previewing in DevFiles viewer. Thanks [@PesBandi](https://github.com/PesBandi)!
- Fixed a crash when the preview handlers received a 64-bit handle from the OS. Thanks [@z4pf1sh](https://github.com/z4pf1sh)!
- Fixed a crash when trying to update window bounds and File Explorer already disposed the preview.
### Find My Mouse
- Added the option to have to use the Windows + Control keys to activate. Thanks [@Gentoli](https://github.com/Gentoli)!
### Hosts File Editor
- Refactored, packaged and released the main Hosts File Editor functionality as a control to be integrated into DevHome. Thanks [@dabhattimsft](https://github.com/dabhattimsft) for validating and integrating into DevHome!
### Image Resizer
- Supported narrator announcing the checkboxes in the UI and the sizes combobox. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Improved accessibility by increasing contrast in the text color of combobox items.
- Improved spacing definitions in the UI so that hosts name are not hidden when resizing and icons are well aligned. Thanks [@htcfreek](https://github.com/htcfreek)!
- Changed the additional lines dialog to show the horizontal scrollbar instead of wrapping contents. Thanks [@htcfreek](https://github.com/htcfreek)!
- Improved the duplication check's logic to improve performance and take into account features that were introduced after it. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
### Installer
- Fixed some install failures when the folders the DSC module is to be installed in isn't accessible by the WiX installer. (This was a hotfix for 0.80)
- Detecting install location for DSC now uses registry instead of WMI to improve performance. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Fixed an error causing the machine scope installer to not install correctly in machines where the documents folder is in a UNC network path. We're still working in a fix for the user scope installer.
### Keyboard Manager
- Fixed startup crashes in the editor when the Visual C++ Redistributable wasn't installed. (This was a hotfix for 0.80)
- Fixed an accessibility issue where the first button wasn't focused after adding a new row in the editor.
- Environment Variables are now expanded in arguments of programs started through a shortcut. Thanks [@HydroH](https://github.com/HydroH)!
### Paste as Plain Text
- Paste as Plain Text was removed as a separate utility, since its functionality is now part of the Advanced Paste utility.
- Fixed the remaining install failures when the folders the DSC module is to be installed in isn't accessible by the WiX installer for user scope installations.
- Fixed an issue causing ARM 64 uninstall process to not correctly finding powershell 7 to run uninstall scripts.
### Peek
- Updated icons, tweaked UI and refactored internal code. Thanks [@Jay-o-Way](https://github.com/Jay-o-Way)!
- Updated Monaco to 0.47 and added the new sticky scroll setting for DevFiles viewer. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)!
- Added the new font size setting for DevFiles viewer. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)!
- Upgrade the SharpCompress dependency to 0.37.2 and fixed archive parsing. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Fixed aliasing in the image viewer.
- Added support for .srt (subtitle) file previewing in DevFiles viewer. Thanks [@PesBandi](https://github.com/PesBandi)!
- Prevent activating Peek when the user is renaming a file. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Added support to preview special folders like Recycle Bin and My PC instead of throwing an error.
- Fixed a crash caused by double releasing a COM object from the module interface.
### Power Rename
- Fixed the descriptions that were mixed up in the regex helper (\S and \w).
- Improved apostrophe character handling for the Capitalize and Titlecase renaming flags. Thanks [@anthonymonforte](https://github.com/anthonymonforte)!
- Added a feature to allow using sequences of random characters or UUIDs when renaming files. Thanks [@jhirvioja](https://github.com/jhirvioja)!
### PowerToys Run
- Added support for UNC paths starting with // in the Folder plugin. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Fixed the plugin load failed message to list the failed plugins. Thanks [@belkiss](https://github.com/belkiss)!
- Icons for MSIX packages are now updated when a package update is detected. Thanks [@HydroH](https://github.com/HydroH)!
- Use Mica backdrop instead of Acrylic to fix random crashes caused by the Windows composition being momentarily turned off.
- Improved accessibility in the results list action buttons by improving contrast of hovered/focused buttons.
- Improved the plugin descriptions for consistency in the UI. Thanks [@HydroH](https://github.com/HydroH)!
- Fixed UI scaling for different dpi scenarios.
- Fixed crash on a racing condition when updating UWP icon paths in the Program plugin. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Fixed PowerToys Run hanging when trying to close an unresponsive window in the WindowWalker plugin. Thanks [@GhostVaibhav](https://github.com/GhostVaibhav)!
- Fixed the example in the UnitConverter description to reduce confusion with the inches abbreviation (now uses "to" instead of "in"). Thanks [@acekirkpatrick](https://github.com/acekirkpatrick)!
- Brought the acrylic background back and applied a proper fix to the titlebar accent showing through transparency.
- Fixed an issue causing the transparency from the UI disappearing after some time.
### Quick Accent
- Added support for the Esperanto character set. Thanks [@salutontalk](https://github.com/salutontalk) and [@ccmywish](https://github.com/ccmywish)!
- Added the ǽ and ϑ characters. Thanks [@PesBandi](https://github.com/PesBandi)!
- Added support for the Crimean Tatar character set. Thanks [@cor-bee](https://github.com/cor-bee)!
- Added the Numero symbol and double acute accent character. Thanks [@PesBandi](https://github.com/PesBandi)!
- Added the International Phonetic Alphabet characters. Thanks [@PesBandi](https://github.com/PesBandi)!
- Fixed the character description center positioning. Thanks [@PesBandi](https://github.com/PesBandi)!
- Added feminine and masculine ordinal indicator characters to the Portuguese character set. Thanks [@octastylos-pseudodipteros](https://github.com/octastylos-pseudodipteros)!
### Registry Preview
### Screen Ruler
- Refactored, packaged and released the main Registry Preview functionality as a control to be integrated into DevHome. Thanks [@dabhattimsft](https://github.com/dabhattimsft) for validating and integrating into DevHome!
### Text Extractor
- Fixed an issue causing the Settings page to not be opened when clicking the Settings button in Text Extractor's overlay. (This was a hotfix for 0.80)
- Updated the default activation hotkey to Win+Control+Shift+M, in order to not conflict with the Windows shortcut that restores minimized windows (Win+Shift+M). Thanks [@nx-frost](https://github.com/nx-frost)!
### Settings
- Improved UI ordering of the File Explorer add-ons. Thanks [@niels9001](https://github.com/niels9001)!
- Applied fixes to theme overriding and cleaned up unneeded code. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Fixed misspells in references to the Hosts File Editor utility. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Improved accessibility of the Select Folder button in the Settings Backup UI.
- Improved accessibility by improving focus and tab navigation in the ColorPicker page. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Added a description to the fallback encoder setting in the Image Resizer page. Thanks [@Kissaki](https://github.com/Kissaki)!
- Refactored and improved performance in the PowerToys Run plugins UI in the Settings page. Thanks [@davidegiacometti](https://github.com/davidegiacometti)!
- Fixed a crash when a user cleared the contents of a Number Box in the PowerToys Run plugins additional options. Thanks [@htcfreek](https://github.com/htcfreek)!
- Update the PATH environment variables with the user scope PATH when entering the Command Not Found page to improve PowerShell detection.
- Disabled the UI to enable/disable clipboard history in the Advanced Paste settings page when clipboard history is disabled by GPO in the system. (This was a hotfix for 0.81)
- Updated Advanced Paste's Settings and OOBE page to clarify that the AI use is optional and opt-in. (This was a hotfix for 0.81)
- Corrected a spelling fix in Advanced Paste's settings page. Thanks [@htcfreek](https://github.com/htcfreek)!
- Added localization support for the "Configure OpenAI Key" button in Advanced Paste's settings page. Thanks [@zetaloop](https://github.com/zetaloop)!
- Fixed extra GPO warnings being shown in Advanced Paste's settings page even if the module is disabled. Thanks [@htcfreek](https://github.com/htcfreek)!
- Fixed a crash when a PowerToys Run plugin icon path is badly formed.
- Disabled the experimentation paths in code behind to improve performance, since there's no current experimentation going on.
### Documentation
- Added the WebSearchShortcut plugin to PowerToys Run thirdPartyRunPlugins.md docs. Thanks [@Daydreamer-riri](https://github.com/Daydreamer-riri)!
- Updated COMMUNITY.md with the project managers that are part of the core team.
- Improved the DSC samples.
- Added the 1Password plugin to PowerToys Run thirdPartyRunPlugins.md docs. Thanks [@KairuDeibisu](https://github.com/KairuDeibisu)!
- Added the UnicodeInput plugin to PowerToys Run thirdPartyRunPlugins.md docs. Thanks [@nathancartlidge](https://github.com/nathancartlidge)!
- Adjusted the readme and release notes to clarify use of AI on Advanced Paste. (This was a hotfix for 0.81)
- Added the Edge Workspaces plugin to PowerToys Run thirdPartyRunPlugins.md docs. Thanks [@quachpas](https://github.com/quachpas)!
- Removed the deprecated Guid plugin from PowerToys Run thirdPartyRunPlugins.md docs. Thanks [@abduljawada](https://github.com/abduljawada)!
- Added the PowerHexInspector plugin to PowerToys Run thirdPartyRunPlugins.md docs. Thanks [@NaroZeol](https://github.com/NaroZeol)!
- Fixed a broken link in the communication-with-modules.md file. Thanks [@PesBandi](https://github.com/PesBandi)!
- Updated COMMUNITY.md with missing and former members.
### Development
- Updated System.Drawing.Common to 8.0.5 to fix CI builds after the .NET 8.0.5 upgrade was released.
- Fixed file permissions when doing a build using cache on PR CI. Thanks [@dfederm](https://github.com/dfederm)!
- Removed the Test SDK reference on ARM64 to fix local building for ARM64. Thanks [@dfederm](https://github.com/dfederm)!
- Replaced make_pair with RemapBufferRow in Keyboard Manager internal code. Thanks [@masaru-iritani](https://github.com/masaru-iritani)!
- Added CODEOWNERS file to protect sensitive parts of the repo. Thanks [@htcfreek](https://github.com/htcfreek) for the help in figuring out how to make the spellcheck folder an exception!
- Added comments in code. to make it clear what the error badge in PowerToys Run plugin list in Settings means. Thanks [@Jay-o-Way](https://github.com/Jay-o-Way)!
- Enabled caching by default in the PR CI pipelines. Thanks [@dfederm](https://github.com/dfederm)!
- Disabled caching for PR started from forks, since those were failing. Thanks [@dfederm](https://github.com/dfederm)!
- Removed baseline files for policy checking and turned on the "TSA" process in the release pipelines instead.
- Added caching of nuget packages in the PR CI pipelines. Thanks [@dfederm](https://github.com/dfederm)!
- Updated the release CI pipelines TouchdownBuildTask to v3.
- Moved the release CI pipelines to ESRPv5.
- Added a policy for GitHub Copilot Workspaces for the repo on GitHub. Thanks [@Aaron-Junker](https://github.com/Aaron-Junker)!
- Fixed ci UI tests to point to the correct Visual Studio vstest location after a Visual Studio upgrade. (This was a hotfix for 0.81)
- Updated System.Drawing.Common to 8.0.6 to fix CI builds after the .NET 8.0.6 upgrade was released.
- Removed an incorrect file reference to long removed documentation from the solution file. Thanks [@Kissaki](https://github.com/Kissaki)!
- Upgraded Windows App SDK to 1.5.3.
- Removed use of the BinaryFormatter API from Mouse Without Borders, which is expected to be deprecated in .NET 9.
- The user scope installer is now sent to the Microsoft store instead of the machine scope installer.
- Refactored Mouse Jump's internal code to allow for a future introduction of customizable appearance features. Thanks [@mikeclayton](https://github.com/mikeclayton)!
- Removed a noisy error from spell check ci runs.
- Improved the ci agent pool selection code.
- Updated Xamlstyler.console to 3.2404.2. Thanks [@Jvr2022](https://github.com/Jvr2022)!
- Updated UnitsNet to 5.50.0 Thanks [@Jvr2022](https://github.com/Jvr2022)!
- Replaced LPINPUT with std::vector of INPUT instances in Keyboard Manager internal code. Thanks [@masaru-iritani](https://github.com/masaru-iritani)!
- Improved the Microsoft Store submission ci action to use the proper cli and authentication.
#### What is being planned for version 0.82
#### What is being planned for version 0.83
For [v0.82][github-next-release-work], we'll work on the items below:
For [v0.83][github-next-release-work], we'll work on the items below:
- Stability / bug fixes
- New utility: Dev Projects
- Language selection
- New module: File Actions Menu

View File

@@ -47,7 +47,16 @@ registerAdditionalNewLanguage("id", [".fileExtension"], idDefinition(), monaco)
* The id can be anything. Recommended is one of the file extensions. For example "php" or "reg".
4. Execute the steps described in the [monaco_languages.json](#monaco_languagesjson) section.
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/common/FilePreviewCommon/Assets/Monaco/customTokenColors.js):
```javascript
{token: 'token-name', foreground: 'ff0000'}
```
> Replace `token-name` with the name of the token and `ff0000` with the hex code of the desired color.
> Note: you can also specify a `background` and a `fontStyle` attribute for your token.
* Keep in mind that these rules apply to all languages. Therefore, you should not change the colors of any default tokens. Instead, create new tokens specific to the language you are adding.
5. Execute the steps described in the [monaco_languages.json](#monaco_languagesjson) section.
### Add a new file extension to an existing language

View File

@@ -72,7 +72,7 @@ In order to test the remapping logic, a mocked keyboard input handler had to be
The [`MockedInput`](https://github.com/microsoft/PowerToys/blob/main/src/modules/keyboardmanager/test/MockedInput.h) class uses a 256 size `bool` vector to store the key state for each key code. Identifying the foreground process is mocked by simply setting and getting a string value for the name of the current process.
[To mock the `SendInput` method](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/test/MockedInput.cpp#L10-L110), the steps for processing the input are as follows. This implementation is based on public documentation for SendInput and the behavior of key messages and keyboard hooks:
- Iterate over all the inputs in the INPUT array argument
- Iterate over all the inputs in the `INPUT` vector argument.
- If the event is a key up event, then it is considered [`WM_SYSKEYUP`](https://learn.microsoft.com/windows/win32/inputdev/wm-syskeyup) if Alt is held down, otherwise it is `WM_KEYUP`.
- If the event is a key down event, then it is considered [`WM_SYSKEYDOWN`](https://learn.microsoft.com/windows/win32/inputdev/wm-syskeydown) if either Alt is held down or if it is F10, otherwise it is `WM_KEYDOWN`.
- An optional function which can be set on the `MockedInput` handler can be used to test for the number of times a key event is received by the system with a particular condition using [`sendVirtualInputCallCondition`](https://github.com/microsoft/PowerToys/blob/b80578b1b9a4b24c9945bddac33c771204280107/src/modules/keyboardmanager/test/MockedInput.cpp#L48-L52).

View File

@@ -18,7 +18,6 @@
<?define AdvancedPasteProjectName="AdvancedPaste"?>
<?define RegistryPreviewProjectName="RegistryPreview"?>
<?define PeekProjectName="Peek"?>
<?define ProjectsProjectName="Projects"?>
<?define RepoDir="$(var.ProjectDir)..\..\" ?>
<?if $(var.Platform) = x64?>

View File

@@ -449,15 +449,6 @@
</RegistryKey>
<File Id="PowerOCR_$(var.IdSafeLanguage)_File" Source="$(var.BinDir)\$(var.Language)\PowerToys.PowerOCR.resources.dll" />
</Component>
<Component
Id="ProjectsEditor_$(var.IdSafeLanguage)_Component"
Directory="Resource$(var.IdSafeLanguage)INSTALLFOLDER"
Guid="$(var.CompGUIDPrefix)21">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="ProjectsEditor_$(var.IdSafeLanguage)_Component" Value="" KeyPath="yes"/>
</RegistryKey>
<File Id="ProjectsEditor_$(var.IdSafeLanguage)_File" Source="$(var.BinDir)\$(var.Language)\PowerToys.ProjectsEditor.resources.dll" />
</Component>
<?undef IdSafeLanguage?>
<?undef CompGUIDPrefix?>
<?endforeach?>

View File

@@ -21,7 +21,7 @@ $fileWxs = Get-Content $wxsFilePath;
$fileExclusionList = @("*.pdb", "*.lastcodeanalysissucceeded", "createdump.exe", "powertoys.exe")
$fileInclusionList = @("*.dll", "*.exe", "*.json", "*.msix", "*.png", "*.gif", "*.ico", "*.cur", "*.svg", "index.html", "reg.js", "gitignore.js", "monacoSpecialLanguages.js", "*.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

@@ -357,7 +357,7 @@ UINT __stdcall InstallDSCModuleCA(MSIHANDLE hInstall)
ExitOnFailure(hr, "Unable to determine Powershell modules path");
}
const auto modulesPath = baseModulesPath / L"Microsoft.PowerToys.Configure" / get_product_version();
const auto modulesPath = baseModulesPath / L"Microsoft.PowerToys.Configure" / get_product_version(false);
std::error_code errorCode;
fs::create_directories(modulesPath, errorCode);
@@ -411,7 +411,7 @@ UINT __stdcall UninstallDSCModuleCA(MSIHANDLE hInstall)
}
const auto powerToysModulePath = baseModulesPath / L"Microsoft.PowerToys.Configure";
const auto versionedModulePath = powerToysModulePath / get_product_version();
const auto versionedModulePath = powerToysModulePath / get_product_version(false);
std::error_code errorCode;
@@ -1223,7 +1223,7 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
}
processes.resize(bytes / sizeof(processes[0]));
std::array<std::wstring_view, 35> processesToTerminate = {
std::array<std::wstring_view, 32> processesToTerminate = {
L"PowerToys.PowerLauncher.exe",
L"PowerToys.Settings.exe",
L"PowerToys.AdvancedPaste.exe",
@@ -1255,9 +1255,6 @@ UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
L"PowerToys.MouseWithoutBordersService.exe",
L"PowerToys.CropAndLock.exe",
L"PowerToys.EnvironmentVariables.exe",
L"PowerToys.ProjectsSnapshotTool.exe",
L"PowerToys.ProjectsLauncher.exe",
L"PowerToys.ProjectsEditor.exe",
L"PowerToys.exe",
};

View File

@@ -12,5 +12,10 @@ namespace Common.UI
{
return Environment.OSVersion.Version.Major >= 10 && Environment.OSVersion.Version.Build >= 22000;
}
public static bool IsGreaterThanWindows11_21H2()
{
return Environment.OSVersion.Version.Major >= 10 && Environment.OSVersion.Version.Build > 22000;
}
}
}

View File

@@ -31,7 +31,6 @@ namespace Common.UI
EnvironmentVariables,
Dashboard,
AdvancedPaste,
Projects,
}
private static string SettingsWindowNameToString(SettingsWindow value)
@@ -78,8 +77,6 @@ namespace Common.UI
return "Dashboard";
case SettingsWindow.AdvancedPaste:
return "AdvancedPaste";
case SettingsWindow.Projects:
return "Projects";
default:
{
return string.Empty;

View File

@@ -24,18 +24,15 @@
<ItemDefinitionGroup>
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<AdditionalIncludeDirectories>..\..\..\;..\..\common;.\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\..\..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="DisplayUtils.h" />
<ClInclude Include="MonitorEnumerator.h" />
<ClInclude Include="monitors.h" />
<ClInclude Include="dpi_aware.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="DisplayUtils.cpp" />
<ClCompile Include="monitors.cpp" />
<ClCompile Include="dpi_aware.cpp" />
</ItemGroup>

View File

@@ -1,147 +0,0 @@
#include "DisplayUtils.h"
#include <algorithm>
#include <iterator>
#include <dpi_aware.h>
#include <MonitorEnumerator.h>
#include <utils/OnThreadExecutor.h>
namespace DisplayUtils
{
constexpr bool not_digit(wchar_t ch)
{
return '0' <= ch && ch <= '9';
}
std::wstring remove_non_digits(const std::wstring& input)
{
std::wstring result;
std::copy_if(input.begin(), input.end(), std::back_inserter(result), not_digit);
return result;
}
std::pair<std::wstring, std::wstring> SplitDisplayDeviceId(const std::wstring& str) noexcept
{
// format: \\?\DISPLAY#{device id}#{instance id}#{some other id}
// example: \\?\DISPLAY#GSM1388#4&125707d6&0&UID8388688#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
// output: { GSM1388, 4&125707d6&0&UID8388688 }
size_t nameStartPos = str.find_first_of('#');
size_t uidStartPos = str.find('#', nameStartPos + 1);
size_t uidEndPos = str.find('#', uidStartPos + 1);
if (nameStartPos == std::string::npos || uidStartPos == std::string::npos || uidEndPos == std::string::npos)
{
return { str, L"" };
}
return { str.substr(nameStartPos + 1, uidStartPos - nameStartPos - 1), str.substr(uidStartPos + 1, uidEndPos - uidStartPos - 1) };
}
std::pair<bool, std::vector<DisplayUtils::DisplayData>> GetDisplays()
{
bool success = true;
std::vector<DisplayUtils::DisplayData> result{};
auto allMonitors = MonitorEnumerator::Enumerate();
OnThreadExecutor dpiUnawareThread;
dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] {
SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE);
SetThreadDpiHostingBehavior(DPI_HOSTING_BEHAVIOR_MIXED);
} }).wait();
for (auto& monitorData : allMonitors)
{
MONITORINFOEX monitorInfo = monitorData.second;
MONITORINFOEX dpiUnawareMonitorInfo{};
dpiUnawareThread.submit(OnThreadExecutor::task_t{ [&] {
dpiUnawareMonitorInfo.cbSize = sizeof(dpiUnawareMonitorInfo);
if (!GetMonitorInfo(monitorData.first, &dpiUnawareMonitorInfo))
{
return;
}
} }).wait();
UINT dpi = 0;
if (DPIAware::GetScreenDPIForMonitor(monitorData.first, dpi) != S_OK)
{
success = false;
break;
}
DisplayUtils::DisplayData data{
.monitor = monitorData.first,
.dpi = dpi,
.monitorRectDpiAware = monitorInfo.rcMonitor,
.monitorRectDpiUnaware = dpiUnawareMonitorInfo.rcMonitor,
};
bool foundActiveMonitor = false;
DISPLAY_DEVICE displayDevice{ .cb = sizeof(displayDevice) };
DWORD displayDeviceIndex = 0;
while (EnumDisplayDevicesW(monitorInfo.szDevice, displayDeviceIndex, &displayDevice, EDD_GET_DEVICE_INTERFACE_NAME))
{
/*
* if (WI_IsFlagSet(displayDevice.StateFlags, DISPLAY_DEVICE_ACTIVE) &&
WI_IsFlagClear(displayDevice.StateFlags, DISPLAY_DEVICE_MIRRORING_DRIVER))
*/
if (((displayDevice.StateFlags & DISPLAY_DEVICE_ACTIVE) == DISPLAY_DEVICE_ACTIVE) &&
(displayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) == 0)
{
// Find display devices associated with the display.
foundActiveMonitor = true;
break;
}
displayDeviceIndex++;
}
if (foundActiveMonitor)
{
auto deviceId = SplitDisplayDeviceId(displayDevice.DeviceID);
data.id = deviceId.first;
data.instanceId = deviceId.second;
try
{
std::wstring numberStr = displayDevice.DeviceName; // \\.\DISPLAY1\Monitor0
numberStr = numberStr.substr(0, numberStr.find_last_of('\\')); // \\.\DISPLAY1
numberStr = remove_non_digits(numberStr);
data.number = std::stoi(numberStr);
}
catch (...)
{
success = false;
break;
}
}
else
{
success = false;
// Use the display name as a fallback value when no proper device was found.
data.id = monitorInfo.szDevice;
data.instanceId = L"";
try
{
std::wstring numberStr = monitorInfo.szDevice; // \\.\DISPLAY1
numberStr = remove_non_digits(numberStr);
data.number = std::stoi(numberStr);
}
catch (...)
{
success = false;
break;
}
}
result.push_back(data);
}
return { success, result };
}
}

View File

@@ -1,21 +0,0 @@
#pragma once
#include <Windows.h>
#include <string>
#include <vector>
namespace DisplayUtils
{
struct DisplayData
{
HMONITOR monitor{};
std::wstring id;
std::wstring instanceId;
unsigned int number{};
unsigned int dpi{};
RECT monitorRectDpiAware{};
RECT monitorRectDpiUnaware{};
};
std::pair<bool, std::vector<DisplayData>> GetDisplays();
};

View File

@@ -1,35 +0,0 @@
#pragma once
#include <functional>
#include <vector>
#include <Windows.h>
class MonitorEnumerator
{
public:
static std::vector<std::pair<HMONITOR, MONITORINFOEX>> Enumerate()
{
MonitorEnumerator inst;
EnumDisplayMonitors(NULL, NULL, Callback, reinterpret_cast<LPARAM>(&inst));
return inst.m_monitors;
}
private:
MonitorEnumerator() = default;
~MonitorEnumerator() = default;
static BOOL CALLBACK Callback(HMONITOR monitor, HDC /*hdc*/, LPRECT /*pRect*/, LPARAM param)
{
MonitorEnumerator* inst = reinterpret_cast<MonitorEnumerator*>(param);
MONITORINFOEX mi;
mi.cbSize = sizeof(mi);
if (GetMonitorInfo(monitor, &mi))
{
inst->m_monitors.push_back({monitor, mi});
}
return TRUE;
}
std::vector<std::pair<HMONITOR, MONITORINFOEX>> m_monitors;
};

View File

@@ -1,9 +1,7 @@
#include "dpi_aware.h"
#include "monitors.h"
#include <ShellScalingApi.h>
#include <array>
#include <cmath>
namespace DPIAware
{
@@ -62,24 +60,6 @@ namespace DPIAware
}
}
void Convert(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>(dpi_x) / DEFAULT_DPI));
rect.right = static_cast<long>(std::round(rect.right * static_cast<float>(dpi_x) / DEFAULT_DPI));
rect.top = static_cast<long>(std::round(rect.top * static_cast<float>(dpi_y) / DEFAULT_DPI));
rect.bottom = static_cast<long>(std::round(rect.bottom * static_cast<float>(dpi_y) / DEFAULT_DPI));
}
}
void ConvertByCursorPosition(float& width, float& height)
{
HMONITOR targetMonitor = nullptr;

View File

@@ -12,7 +12,6 @@ namespace DPIAware
HRESULT GetScreenDPIForPoint(POINT p, UINT& dpi);
HRESULT GetScreenDPIForCursor(UINT& dpi);
void Convert(HMONITOR monitor_handle, float& width, float& height);
void Convert(HMONITOR monitor_handle, RECT& rect);
void ConvertByCursorPosition(float& width, float& height);
void InverseConvert(HMONITOR monitor_handle, float& width, float& height);
void EnableDPIAwarenessForThisProcess();

View File

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

View File

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

View File

@@ -6,7 +6,7 @@
// Get URL parameters:
// `code` contains the code of the file in base64 encoded
// `theme` can be "light" or "dark"
// `theme` can be "vs" for light theme or "vs-dark" for dark theme
// `lang` is the language of the file
// `wrap` if the editor is wrapping or not
@@ -59,19 +59,29 @@
<script src="http://[[PT_URL]]/monacoSpecialLanguages.js" type="module"></script>
<script type="module">
var editor;
import { registerAdditionalLanguages } from "http://[[PT_URL]]/monacoSpecialLanguages.js"
import { registerAdditionalLanguages } from 'http://[[PT_URL]]/monacoSpecialLanguages.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)
// Creates a theme to handle custom tokens
monaco.editor.defineTheme('theme', {
base: theme, // Sets the base theme to "vs" or "vs-dark" depending on the user's preference
inherit: true,
rules: customTokenColors,
colors: {} // `colors` is a required attribute
});
// Creates the editor
// For all parameters: https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.istandaloneeditorconstructionoptions.html
// For all parameters: https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor.IStandaloneEditorConstructionOptions.html
editor = monaco.editor.create(document.getElementById('container'), {
value: code, // Sets content of the editor
language: lang, // Sets language fof the code
language: lang, // Sets language of the code
readOnly: true, // Sets to readonly
theme: theme, // Sets editor theme
theme: 'theme', // Sets editor theme
minimap: {enabled: false}, // Disables minimap
lineNumbersMinChars: "3", //Width of the line numbers
lineNumbersMinChars: '3', // Width of the line numbers
scrollbar: {
// Deactivate shadows
shadows: false,
@@ -79,17 +89,16 @@
// Render scrollbar automatically
vertical: 'auto',
horizontal: 'auto',
},
stickyScroll: {enabled: stickyScroll},
fontSize: fontSize,
wordWrap: (wrap?"on":"off") // Word wraps
wordWrap: (wrap ? 'on' : 'off') // Word wraps
});
window.onresize = function (){
window.onresize = () => {
editor.layout();
};
// Add switch wrap button to context menu
// Add toggle wrap button to context menu
editor.addAction({
id: 'text-wrap',
@@ -101,17 +110,17 @@
// A rule to evaluate on top of the precondition in order to dispatch the keybindings.
keybindingContext: null,
contextMenuGroupId: 'cutcopypaste',
contextMenuGroupId: 'cutcopypaste',
contextMenuOrder: 100,
contextMenuOrder: 100,
// Method that will be executed when the action is triggered.
// @param editor The editor instance is passed in as a convenience
run: function (ed) {
if(wrap){
editor.updateOptions({ wordWrap: "off" })
}else{
editor.updateOptions({ wordWrap: "on" })
if (wrap) {
editor.updateOptions({ wordWrap: 'off' })
} else {
editor.updateOptions({ wordWrap: 'on' })
}
wrap = !wrap;
}
@@ -120,11 +129,11 @@
onContextMenu();
});
function onContextMenu(){
function onContextMenu() {
// Hide context menu items
// Code modified from https://stackoverflow.com/questions/48745208/disable-cut-and-copy-in-context-menu-in-monaco-editor/65413517#65413517
let menus = require('vs/platform/actions/common/actions').MenuRegistry._menuItems
let contextMenuEntry = [...menus].find(entry => entry[0].id == "EditorContext")
let contextMenuEntry = [...menus].find(entry => entry[0].id == 'EditorContext')
let contextMenuLinks = contextMenuEntry[1]
let removableIds = ['editor.action.clipboardCutAction', 'editor.action.formatDocument', 'editor.action.formatSelection', 'editor.action.quickCommand', 'editor.action.quickOutline', 'editor.action.refactor', 'editor.action.sourceAction', 'editor.action.rename', undefined, 'editor.action.revealDefinition', 'editor.action.revealDeclaration', 'editor.action.goToTypeDefinition', 'editor.action.goToImplementation', 'editor.action.goToReferences', 'editor.action.changeAll']

View File

@@ -34,6 +34,9 @@
<None Update="Assets\Monaco\monacoSpecialLanguages.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Assets\Monaco\customTokenColors.js">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<Content Include="Assets\Monaco\monacoSRC\**">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>

View File

@@ -1,4 +1,4 @@
#include "pch.h"
#include "pch.h"
#include "GPOWrapper.h"
#include "GPOWrapper.g.cpp"
@@ -176,8 +176,4 @@ namespace winrt::PowerToys::GPOWrapper::implementation
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getAllowedAdvancedPasteOnlineAIModelsValue());
}
GpoRuleConfigured GPOWrapper::GetConfiguredProjectsEnabledValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredProjectsEnabledValue());
}
}

View File

@@ -1,4 +1,4 @@
#pragma once
#pragma once
#include "GPOWrapper.g.h"
#include <common/utils/gpo.h>
@@ -50,7 +50,6 @@ namespace winrt::PowerToys::GPOWrapper::implementation
static GpoRuleConfigured GetConfiguredQoiPreviewEnabledValue();
static GpoRuleConfigured GetConfiguredQoiThumbnailsEnabledValue();
static GpoRuleConfigured GetAllowedAdvancedPasteOnlineAIModelsValue();
static GpoRuleConfigured GetConfiguredProjectsEnabledValue();
};
}

View File

@@ -54,7 +54,6 @@ namespace PowerToys
static GpoRuleConfigured GetConfiguredQoiPreviewEnabledValue();
static GpoRuleConfigured GetConfiguredQoiThumbnailsEnabledValue();
static GpoRuleConfigured GetAllowedAdvancedPasteOnlineAIModelsValue();
static GpoRuleConfigured GetConfiguredProjectsEnabledValue();
}
}
}

View File

@@ -61,10 +61,5 @@ namespace PowerToys.GPOWrapperProjection
{
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetRunPluginEnabledValue(pluginID);
}
public static GpoRuleConfigured GetConfiguredProjectsEnabledValue()
{
return (GpoRuleConfigured)PowerToys.GPOWrapper.GPOWrapper.GetConfiguredProjectsEnabledValue();
}
}
}

View File

@@ -26,7 +26,6 @@ namespace ManagedCommon
PowerRename,
PowerLauncher,
PowerAccent,
Projects,
RegistryPreview,
MeasureTool,
ShortcutGuide,

View File

@@ -96,8 +96,6 @@ namespace CommonSharedConstants
const wchar_t SHOW_ENVIRONMENT_VARIABLES_EVENT[] = L"Local\\PowerToysEnvironmentVariables-ShowEnvironmentVariablesEvent-1021f616-e951-4d64-b231-a8f972159978";
const wchar_t SHOW_ENVIRONMENT_VARIABLES_ADMIN_EVENT[] = L"Local\\PowerToysEnvironmentVariables-EnvironmentVariablesAdminEvent-8c95d2ad-047c-49a2-9e8b-b4656326cfb2";
const wchar_t PROJECTS_EXIT_EVENT[] = L"Local\\PowerToys-Projects-ExitEvent-29a1566f-f4f8-4d56-9435-d2a437f727c6";
// Max DWORD for key code to disable keys.
const DWORD VK_DISABLED = 0x100;
}

View File

@@ -69,10 +69,6 @@ struct LogSettings
inline const static std::string environmentVariablesLoggerName = "environment-variables";
inline const static std::wstring cmdNotFoundLogPath = L"Logs\\cmd-not-found-log.txt";
inline const static std::string cmdNotFoundLoggerName = "cmd-not-found";
inline const static std::string projectsLauncherLoggerName = "projects-launcher";
inline const static std::wstring projectsLauncherLogPath = L"projects-launcher-log.txt";
inline const static std::string projectsSnapshotToolLoggerName = "projects-snapshot-tool";
inline const static std::wstring projectsSnapshotToolLogPath = L"projects-snapshot-tool-log.txt";
inline const static int retention = 30;
std::wstring logLevel;
LogSettings();

View File

@@ -1,72 +0,0 @@
#pragma once
#include <future>
#include <thread>
#include <functional>
#include <queue>
#include <atomic>
// OnThreadExecutor allows its caller to off-load some work to a persistently running background thread.
// This might come in handy if you use the API which sets thread-wide global state and the state needs
// to be isolated.
class OnThreadExecutor final
{
public:
using task_t = std::packaged_task<void()>;
OnThreadExecutor() :
_shutdown_request{ false },
_worker_thread{ [this] { worker_thread(); } }
{
}
~OnThreadExecutor()
{
_shutdown_request = true;
_task_cv.notify_one();
_worker_thread.join();
}
std::future<void> submit(task_t task)
{
auto future = task.get_future();
std::lock_guard lock{ _task_mutex };
_task_queue.emplace(std::move(task));
_task_cv.notify_one();
return future;
}
void cancel()
{
std::lock_guard lock{ _task_mutex };
_task_queue = {};
_task_cv.notify_one();
}
private:
void worker_thread()
{
while (!_shutdown_request)
{
task_t task;
{
std::unique_lock task_lock{ _task_mutex };
_task_cv.wait(task_lock, [this] { return !_task_queue.empty() || _shutdown_request; });
if (_shutdown_request)
{
return;
}
task = std::move(_task_queue.front());
_task_queue.pop();
}
task();
}
}
std::mutex _task_mutex;
std::condition_variable _task_cv;
std::atomic_bool _shutdown_request;
std::queue<std::packaged_task<void()>> _task_queue;
std::thread _worker_thread;
};

View File

@@ -59,7 +59,6 @@ namespace powertoys_gpo {
const std::wstring POLICY_CONFIGURE_ENABLED_ENVIRONMENT_VARIABLES = L"ConfigureEnabledUtilityEnvironmentVariables";
const std::wstring POLICY_CONFIGURE_ENABLED_QOI_PREVIEW = L"ConfigureEnabledUtilityFileExplorerQOIPreview";
const std::wstring POLICY_CONFIGURE_ENABLED_QOI_THUMBNAILS = L"ConfigureEnabledUtilityFileExplorerQOIThumbnails";
const std::wstring POLICY_CONFIGURE_ENABLED_PROJECTS = L"ConfigureEnabledUtilityProjects";
// The registry value names for PowerToys installer and update policies.
const std::wstring POLICY_DISABLE_PER_USER_INSTALLATION = L"PerUserInstallationDisabled";
@@ -367,11 +366,6 @@ namespace powertoys_gpo {
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_ADVANCED_PASTE);
}
inline gpo_rule_configured_t getConfiguredProjectsEnabledValue()
{
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_PROJECTS);
}
inline gpo_rule_configured_t getConfiguredVideoConferenceMuteEnabledValue()
{
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_VIDEO_CONFERENCE_MUTE);

View File

@@ -27,18 +27,18 @@ enum class version_architecture
version_architecture get_current_architecture();
const wchar_t* get_architecture_string(const version_architecture);
inline std::wstring get_product_version()
inline std::wstring get_product_version(bool includeV = true)
{
static std::wstring version = L"v" + std::to_wstring(VERSION_MAJOR) +
static std::wstring version = (includeV ? L"v" : L"") + std::to_wstring(VERSION_MAJOR) +
L"." + std::to_wstring(VERSION_MINOR) +
L"." + std::to_wstring(VERSION_REVISION);
return version;
}
inline std::wstring get_std_product_version()
inline std::wstring get_std_product_version(bool includeV = true)
{
static std::wstring version = L"v" + std::to_wstring(VERSION_MAJOR) +
static std::wstring version = (includeV ? L"v" : L"") + std::to_wstring(VERSION_MAJOR) +
L"." + std::to_wstring(VERSION_MINOR) +
L"." + std::to_wstring(VERSION_REVISION) + L".0";

View File

@@ -57,8 +57,6 @@ properties:
EnableQoiThumbnail: false
PowerOcr:
Enabled: false
Projects:
Enabled: false
ShortcutGuide:
Enabled: false
VideoConference:

View File

@@ -57,8 +57,6 @@ properties:
EnableQoiThumbnail: true
PowerOcr:
Enabled: true
Projects:
Enabled: true
ShortcutGuide:
Enabled: true
VideoConference:

View File

@@ -356,16 +356,6 @@
<decimal value="0" />
</disabledValue>
</policy>
<policy name="ConfigureEnabledUtilityProjects" class="Both" displayName="$(string.ConfigureEnabledUtilityProjects)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityProjects">
<parentCategory ref="PowerToys" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_81_0" />
<enabledValue>
<decimal value="1" />
</enabledValue>
<disabledValue>
<decimal value="0" />
</disabledValue>
</policy>
<policy name="ConfigureEnabledUtilityQuickAccent" class="Both" displayName="$(string.ConfigureEnabledUtilityQuickAccent)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityQuickAccent">
<parentCategory ref="PowerToys" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_64_0" />

View File

@@ -10,7 +10,6 @@
<string id="InstallerUpdates">Installer and Updates</string>
<string id="PowerToysRun">PowerToys Run</string>
<string id="AdvancedPaste">Advanced Paste</string>
<string id="Projects">Projects</string>
<string id="SUPPORTED_POWERTOYS_0_64_0">PowerToys version 0.64.0 or later</string>
<string id="SUPPORTED_POWERTOYS_0_68_0">PowerToys version 0.68.0 or later</string>
@@ -108,12 +107,6 @@ If you don't configure this setting, users are able to enable or disable the plu
You can override this policy for individual plugins using the policy "Configure enabled state for individual plugins".
Note: Changes require a restart of PowerToys Run.
</string>
<string id="ConfigureEnabledUtilityProjects">This policy configures the enabled disable state for the Projects utility.
If you enable or don't configure this policy, the user takes control over the enabled state of the Projects utility.
If you disable this policy, the user won't be able to enable Enable and use the Projects utility.
</string>
<string id="PowerToysRunIndividualPluginEnabledStateDescription">With this policy you can configure an individual enabled state for each PowerToys Run plugin that you add to the list.
@@ -164,7 +157,6 @@ If you disable this policy, the user won't be able to enable Enable paste with A
<string id="ConfigureEnabledUtilityPeek">Peek: Configure enabled state</string>
<string id="ConfigureEnabledUtilityPowerRename">Power Rename: Configure enabled state</string>
<string id="ConfigureEnabledUtilityPowerLauncher">PowerToys Run: Configure enabled state</string>
<string id="ConfigureEnabledUtilityProjects">PowerToys Projects: Configure enabled state</string>
<string id="ConfigureEnabledUtilityQuickAccent">Quick Accent: Configure enabled state</string>
<string id="ConfigureEnabledUtilityRegistryPreview">Registry Preview: Configure enabled state</string>
<string id="ConfigureEnabledUtilityScreenRuler">Screen Ruler: Configure enabled state</string>

View File

@@ -3,15 +3,14 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Threading.Tasks;
using AdvancedPaste.Helpers;
using AdvancedPaste.Settings;
using AdvancedPaste.ViewModels;
using ManagedCommon;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Windows.ApplicationModel.DataTransfer;
using Windows.Graphics;
using WinUIEx;
using static AdvancedPaste.Helpers.NativeMethods;
@@ -47,6 +46,7 @@ namespace AdvancedPaste
Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder().UseContentRoot(AppContext.BaseDirectory).ConfigureServices((context, services) =>
{
services.AddSingleton<OptionsViewModel>();
services.AddSingleton<IUserSettings, UserSettings>();
}).Build();
viewModel = GetService<OptionsViewModel>();

View File

@@ -2,7 +2,6 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Net;
using System.Threading.Tasks;
using AdvancedPaste.Helpers;
@@ -20,14 +19,13 @@ namespace AdvancedPaste.Controls
public sealed partial class PromptBox : Microsoft.UI.Xaml.Controls.UserControl
{
private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
private UserSettings _userSettings;
private readonly IUserSettings _userSettings;
public static readonly DependencyProperty PromptProperty = DependencyProperty.Register(
nameof(Prompt),
typeof(string),
typeof(PromptBox),
new PropertyMetadata(defaultValue: string.Empty));
nameof(Prompt),
typeof(string),
typeof(PromptBox),
new PropertyMetadata(defaultValue: string.Empty));
public OptionsViewModel ViewModel { get; private set; }
@@ -38,10 +36,10 @@ namespace AdvancedPaste.Controls
}
public static readonly DependencyProperty PlaceholderTextProperty = DependencyProperty.Register(
nameof(PlaceholderText),
typeof(string),
typeof(PromptBox),
new PropertyMetadata(defaultValue: string.Empty));
nameof(PlaceholderText),
typeof(string),
typeof(PromptBox),
new PropertyMetadata(defaultValue: string.Empty));
public string PlaceholderText
{
@@ -50,10 +48,10 @@ namespace AdvancedPaste.Controls
}
public static readonly DependencyProperty FooterProperty = DependencyProperty.Register(
nameof(Footer),
typeof(object),
typeof(PromptBox),
new PropertyMetadata(defaultValue: null));
nameof(Footer),
typeof(object),
typeof(PromptBox),
new PropertyMetadata(defaultValue: null));
public object Footer
{
@@ -65,7 +63,7 @@ namespace AdvancedPaste.Controls
{
this.InitializeComponent();
_userSettings = new UserSettings();
_userSettings = App.GetService<IUserSettings>();
ViewModel = App.GetService<OptionsViewModel>();
}
@@ -80,8 +78,6 @@ namespace AdvancedPaste.Controls
{
Logger.LogTrace();
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteGenerateCustomFormatEvent());
VisualStateManager.GoToState(this, "LoadingState", true);
string inputInstructions = InputTxtBox.Text;
ViewModel.SaveQuery(inputInstructions);

View File

@@ -3,21 +3,20 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.CompilerServices;
using AdvancedPaste.Helpers;
using AdvancedPaste.Settings;
using ManagedCommon;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Windows.Graphics;
using WinUIEx;
using WinUIEx.Messaging;
using static AdvancedPaste.Helpers.NativeMethods;
namespace AdvancedPaste
{
public sealed partial class MainWindow : WindowEx, IDisposable
{
private WindowMessageMonitor _msgMonitor;
private readonly WindowMessageMonitor _msgMonitor;
private readonly IUserSettings _userSettings;
private bool _disposedValue;
@@ -25,6 +24,8 @@ namespace AdvancedPaste
{
this.InitializeComponent();
_userSettings = App.GetService<IUserSettings>();
AppWindow.SetIcon("Assets/AdvancedPaste/AdvancedPaste.ico");
this.ExtendsContentIntoTitleBar = true;
this.SetTitleBar(titleBar);
@@ -32,6 +33,8 @@ namespace AdvancedPaste
var loader = ResourceLoaderInstance.ResourceLoader;
Title = loader.GetString("WindowTitle");
Activated += OnActivated;
_msgMonitor = new WindowMessageMonitor(this);
_msgMonitor.WindowMessageReceived += (_, e) =>
{
@@ -47,6 +50,14 @@ namespace AdvancedPaste
WindowHelpers.BringToForeground(this.GetWindowHandle());
}
private void OnActivated(object sender, WindowActivatedEventArgs args)
{
if (_userSettings.CloseAfterLosingFocus && args.WindowActivationState == WindowActivationState.Deactivated)
{
Hide();
}
}
private void Dispose(bool disposing)
{
if (!_disposedValue)
@@ -66,11 +77,15 @@ namespace AdvancedPaste
private void WindowEx_Closed(object sender, Microsoft.UI.Xaml.WindowEventArgs args)
{
Windows.Win32.PInvoke.ShowWindow((Windows.Win32.Foundation.HWND)this.GetWindowHandle(), Windows.Win32.UI.WindowsAndMessaging.SHOW_WINDOW_CMD.SW_HIDE);
Hide();
args.Handled = true;
}
private void Hide()
{
Windows.Win32.PInvoke.ShowWindow(new Windows.Win32.Foundation.HWND(this.GetWindowHandle()), Windows.Win32.UI.WindowsAndMessaging.SHOW_WINDOW_CMD.SW_HIDE);
}
public void SetFocus()
{
MainPage.CustomFormatTextBox.InputTxtBox.Focus(FocusState.Programmatic);

View File

@@ -5,6 +5,7 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AdvancedPaste.Helpers;
@@ -86,6 +87,11 @@ namespace AdvancedPaste.Pages
_dispatcherQueue.TryEnqueue(async () =>
{
// Clear to avoid leaks due to Garbage Collection not clearing the bitmap from memory. Fix for https://github.com/microsoft/PowerToys/issues/33423
clipboardHistory.Where(x => x.Image is not null)
.ToList()
.ForEach(x => x.Image.ClearValue(BitmapImage.UriSourceProperty));
clipboardHistory.Clear();
foreach (var item in items)
@@ -225,6 +231,7 @@ namespace AdvancedPaste.Pages
var item = e.ClickedItem as ClipboardItem;
if (item is not null)
{
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteClipboardItemClicked());
if (!string.IsNullOrEmpty(item.Content))
{
ClipboardHelper.SetClipboardTextContent(item.Content);

View File

@@ -33,6 +33,8 @@ namespace AdvancedPaste.Helpers
private string _openAIKey;
private string _modelName = "gpt-3.5-turbo-instruct";
public bool IsAIEnabled => !string.IsNullOrEmpty(this._openAIKey);
public AICompletionsHelper()
@@ -69,14 +71,14 @@ namespace AdvancedPaste.Helpers
return string.Empty;
}
public string GetAICompletion(string systemInstructions, string userMessage)
private Response<Completions> GetAICompletion(string systemInstructions, string userMessage)
{
OpenAIClient azureAIClient = new OpenAIClient(_openAIKey);
var response = azureAIClient.GetCompletions(
new CompletionsOptions()
{
DeploymentName = "gpt-3.5-turbo-instruct",
DeploymentName = _modelName,
Prompts =
{
systemInstructions + "\n\n" + userMessage,
@@ -90,7 +92,7 @@ namespace AdvancedPaste.Helpers
Console.WriteLine("Cut off due to length constraints");
}
return response.Value.Choices[0].Text;
return response;
}
public AICompletionsResponse AIFormatString(string inputInstructions, string inputString)
@@ -109,10 +111,16 @@ Output:
";
string aiResponse = null;
Response<Completions> rawAIResponse = null;
int apiRequestStatus = (int)HttpStatusCode.OK;
try
{
aiResponse = this.GetAICompletion(systemInstructions, userMessage);
rawAIResponse = this.GetAICompletion(systemInstructions, userMessage);
aiResponse = rawAIResponse.Value.Choices[0].Text;
int promptTokens = rawAIResponse.Value.Usage.PromptTokens;
int completionTokens = rawAIResponse.Value.Usage.CompletionTokens;
PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteGenerateCustomFormatEvent(promptTokens, completionTokens, _modelName));
}
catch (Azure.RequestFailedException error)
{

View File

@@ -9,5 +9,7 @@ namespace AdvancedPaste.Settings
public bool ShowCustomPreview { get; }
public bool SendPasteKeyCombination { get; }
public bool CloseAfterLosingFocus { get; }
}
}

View File

@@ -16,6 +16,10 @@ namespace AdvancedPaste.Helpers
{
internal static class JsonHelper
{
// Ini parts regex
private static readonly Regex IniSectionNameRegex = new Regex(@"^\[(.+)\]");
private static readonly Regex IniValueLineRegex = new Regex(@"(.+?)\s*=\s*(.*)");
// List of supported CSV delimiters and Regex to detect separator property
private static readonly char[] CsvDelimArry = [',', ';', '\t'];
private static readonly Regex CsvSepIdentifierRegex = new Regex(@"^sep=(.)$", RegexOptions.IgnoreCase);
@@ -45,6 +49,7 @@ namespace AdvancedPaste.Helpers
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(text);
Logger.LogDebug("Converted from XML.");
jsonText = JsonConvert.SerializeXmlNode(doc, Newtonsoft.Json.Formatting.Indented);
}
catch (Exception ex)
@@ -52,6 +57,75 @@ namespace AdvancedPaste.Helpers
Logger.LogError("Failed parsing input as xml", ex);
}
// Try convert Ini
// (Must come before CSV that ini is not false detected as CSV.)
try
{
if (string.IsNullOrEmpty(jsonText))
{
var ini = new Dictionary<string, Dictionary<string, string>>();
var lastSectionName = string.Empty;
string[] lines = text.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
// Skipp comment lines.
// (Comments are lines that starts with a semicolon.
// This also skips commented key-value-pairs.)
lines = lines.Where(l => !l.StartsWith(';')).ToArray();
// Validate content as ini
// (First line is a section name and second line is a section name or a key-value-pair.
// For the second line we check both, in case the first ini section is empty.)
if (lines.Length >= 2 && IniSectionNameRegex.IsMatch(lines[0]) &&
(IniSectionNameRegex.IsMatch(lines[1]) || IniValueLineRegex.IsMatch(lines[1])))
{
// Parse and convert Ini
foreach (string line in lines)
{
Match lineSectionNameCheck = IniSectionNameRegex.Match(line);
Match lineKeyValuePairCheck = IniValueLineRegex.Match(line);
if (lineSectionNameCheck.Success)
{
// Section name (Group 1)
lastSectionName = lineSectionNameCheck.Groups[1].Value.Trim();
if (string.IsNullOrWhiteSpace(lastSectionName))
{
throw new FormatException("Invalid ini file format: Empty section name.");
}
ini.Add(lastSectionName, new Dictionary<string, string>());
}
else if (!lineKeyValuePairCheck.Success)
{
// Fail if it is not a key-value-pair (and was not detected as section name before).
throw new FormatException("Invalid ini file format: Invalid line.");
}
else
{
// Key-value-pair (Group 1=Key; Group 2=Value)
string iniKeyName = lineKeyValuePairCheck.Groups[1].Value.Trim();
if (string.IsNullOrWhiteSpace(iniKeyName))
{
throw new FormatException("Invalid ini file format: Empty value name (key).");
}
string iniValueData = lineKeyValuePairCheck.Groups[2].Value;
ini[lastSectionName].Add(iniKeyName, iniValueData);
}
}
// Convert to JSON
Logger.LogDebug("Converted from Ini.");
jsonText = JsonConvert.SerializeObject(ini, Newtonsoft.Json.Formatting.Indented);
}
}
}
catch (Exception ex)
{
Logger.LogError("Failed parsing input as ini", ex);
}
// Try convert CSV
try
{

View File

@@ -24,12 +24,15 @@ namespace AdvancedPaste.Settings
public bool SendPasteKeyCombination { get; private set; }
public bool CloseAfterLosingFocus { get; private set; }
public UserSettings()
{
_settingsUtils = new SettingsUtils();
ShowCustomPreview = true;
SendPasteKeyCombination = true;
CloseAfterLosingFocus = false;
LoadSettingsFromJson();
@@ -61,6 +64,7 @@ namespace AdvancedPaste.Settings
{
ShowCustomPreview = settings.Properties.ShowCustomPreview;
SendPasteKeyCombination = settings.Properties.SendPasteKeyCombination;
CloseAfterLosingFocus = settings.Properties.CloseAfterLosingFocus;
}
retry = false;

View File

@@ -0,0 +1,16 @@
// 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.Diagnostics.Tracing;
using Microsoft.PowerToys.Telemetry;
using Microsoft.PowerToys.Telemetry.Events;
namespace AdvancedPaste.Telemetry
{
[EventData]
public class AdvancedPasteClipboardItemClicked : EventBase, IEvent
{
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
}
}

View File

@@ -11,6 +11,19 @@ namespace AdvancedPaste.Telemetry
[EventData]
public class AdvancedPasteGenerateCustomFormatEvent : EventBase, IEvent
{
public int PromptTokens { get; set; }
public int CompletionTokens { get; set; }
public string ModelName { get; set; }
public AdvancedPasteGenerateCustomFormatEvent(int promptTokens, int completionTokens, string modelName)
{
this.PromptTokens = promptTokens;
this.CompletionTokens = completionTokens;
ModelName = modelName;
}
public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage;
}
}

View File

@@ -25,13 +25,12 @@ namespace AdvancedPaste.ViewModels
public partial class OptionsViewModel : ObservableObject
{
private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
private readonly IUserSettings _userSettings;
private App app = App.Current as App;
private AICompletionsHelper aiHelper;
private UserSettings _userSettings;
public DataPackageView ClipboardData { get; set; }
[ObservableProperty]
@@ -50,10 +49,10 @@ namespace AdvancedPaste.ViewModels
[NotifyPropertyChangedFor(nameof(InputTxtBoxErrorText))]
private int _apiRequestStatus;
public OptionsViewModel()
public OptionsViewModel(IUserSettings userSettings)
{
aiHelper = new AICompletionsHelper();
_userSettings = new UserSettings();
_userSettings = userSettings;
IsCustomAIEnabled = IsClipboardDataText && aiHelper.IsAIEnabled;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

View File

@@ -1,146 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.9.34622.214
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ProjectsSnapshotTool", "ProjectsSnapshotTool\ProjectsSnapshotTool.vcxproj", "{3D63307B-9D27-44FD-B033-B26F39245B85}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "projects-common", "projects-common", "{BA45247D-3046-408D-BE01-128587A7799F}"
ProjectSection(SolutionItems) = preProject
projects-common\AppUtils.h = projects-common\AppUtils.h
projects-common\Data.h = projects-common\Data.h
projects-common\GuidUtils.h = projects-common\GuidUtils.h
projects-common\json.h = projects-common\json.h
projects-common\MonitorEnumerator.h = projects-common\MonitorEnumerator.h
projects-common\VirtualDesktop.h = projects-common\VirtualDesktop.h
projects-common\WindowEnumerator.h = projects-common\WindowEnumerator.h
projects-common\WindowFilter.h = projects-common\WindowFilter.h
projects-common\WindowUtils.h = projects-common\WindowUtils.h
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ProjectsLauncher", "ProjectsLauncher\ProjectsLauncher.vcxproj", "{2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProjectsEditor", "ProjectsEditor\ProjectsEditor.csproj", "{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedCommon", "..\..\common\ManagedCommon\ManagedCommon.csproj", "{A881F6EB-6EDA-4674-A6B7-598F0A8E7048}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerToys.Interop", "..\..\common\interop\PowerToys.Interop.vcxproj", "{F055103B-F80B-4D0C-BF48-057C55620033}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagedTelemetry", "..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj", "{6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|ARM64 = Release|ARM64
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{3D63307B-9D27-44FD-B033-B26F39245B85}.Debug|Any CPU.ActiveCfg = Debug|x64
{3D63307B-9D27-44FD-B033-B26F39245B85}.Debug|Any CPU.Build.0 = Debug|x64
{3D63307B-9D27-44FD-B033-B26F39245B85}.Debug|ARM64.ActiveCfg = Debug|ARM64
{3D63307B-9D27-44FD-B033-B26F39245B85}.Debug|ARM64.Build.0 = Debug|ARM64
{3D63307B-9D27-44FD-B033-B26F39245B85}.Debug|x64.ActiveCfg = Debug|x64
{3D63307B-9D27-44FD-B033-B26F39245B85}.Debug|x64.Build.0 = Debug|x64
{3D63307B-9D27-44FD-B033-B26F39245B85}.Debug|x86.ActiveCfg = Debug|Win32
{3D63307B-9D27-44FD-B033-B26F39245B85}.Debug|x86.Build.0 = Debug|Win32
{3D63307B-9D27-44FD-B033-B26F39245B85}.Release|Any CPU.ActiveCfg = Release|x64
{3D63307B-9D27-44FD-B033-B26F39245B85}.Release|Any CPU.Build.0 = Release|x64
{3D63307B-9D27-44FD-B033-B26F39245B85}.Release|ARM64.ActiveCfg = Release|ARM64
{3D63307B-9D27-44FD-B033-B26F39245B85}.Release|ARM64.Build.0 = Release|ARM64
{3D63307B-9D27-44FD-B033-B26F39245B85}.Release|x64.ActiveCfg = Release|x64
{3D63307B-9D27-44FD-B033-B26F39245B85}.Release|x64.Build.0 = Release|x64
{3D63307B-9D27-44FD-B033-B26F39245B85}.Release|x86.ActiveCfg = Release|Win32
{3D63307B-9D27-44FD-B033-B26F39245B85}.Release|x86.Build.0 = Release|Win32
{2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Debug|Any CPU.ActiveCfg = Debug|x64
{2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Debug|Any CPU.Build.0 = Debug|x64
{2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Debug|ARM64.ActiveCfg = Debug|ARM64
{2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Debug|ARM64.Build.0 = Debug|ARM64
{2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Debug|x64.ActiveCfg = Debug|x64
{2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Debug|x64.Build.0 = Debug|x64
{2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Debug|x86.ActiveCfg = Debug|Win32
{2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Debug|x86.Build.0 = Debug|Win32
{2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Release|Any CPU.ActiveCfg = Release|x64
{2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Release|Any CPU.Build.0 = Release|x64
{2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Release|ARM64.ActiveCfg = Release|ARM64
{2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Release|ARM64.Build.0 = Release|ARM64
{2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Release|x64.ActiveCfg = Release|x64
{2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Release|x64.Build.0 = Release|x64
{2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Release|x86.ActiveCfg = Release|Win32
{2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96}.Release|x86.Build.0 = Release|Win32
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|ARM64.ActiveCfg = Debug|Any CPU
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|ARM64.Build.0 = Debug|Any CPU
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|x64.ActiveCfg = Debug|x64
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|x64.Build.0 = Debug|x64
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|x86.ActiveCfg = Debug|Any CPU
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|x86.Build.0 = Debug|Any CPU
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|Any CPU.Build.0 = Release|Any CPU
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|ARM64.ActiveCfg = Release|Any CPU
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|ARM64.Build.0 = Release|Any CPU
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|x64.ActiveCfg = Release|x64
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|x64.Build.0 = Release|x64
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|x86.ActiveCfg = Release|Any CPU
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|x86.Build.0 = Release|Any CPU
{A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Debug|Any CPU.ActiveCfg = Debug|x64
{A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Debug|Any CPU.Build.0 = Debug|x64
{A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Debug|ARM64.ActiveCfg = Debug|ARM64
{A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Debug|ARM64.Build.0 = Debug|ARM64
{A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Debug|x64.ActiveCfg = Debug|x64
{A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Debug|x64.Build.0 = Debug|x64
{A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Debug|x86.ActiveCfg = Debug|x64
{A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Debug|x86.Build.0 = Debug|x64
{A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Release|Any CPU.ActiveCfg = Release|x64
{A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Release|Any CPU.Build.0 = Release|x64
{A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Release|ARM64.ActiveCfg = Release|ARM64
{A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Release|ARM64.Build.0 = Release|ARM64
{A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Release|x64.ActiveCfg = Release|x64
{A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Release|x64.Build.0 = Release|x64
{A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Release|x86.ActiveCfg = Release|x64
{A881F6EB-6EDA-4674-A6B7-598F0A8E7048}.Release|x86.Build.0 = Release|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Debug|Any CPU.ActiveCfg = Debug|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Debug|Any CPU.Build.0 = Debug|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Debug|ARM64.ActiveCfg = Debug|ARM64
{F055103B-F80B-4D0C-BF48-057C55620033}.Debug|ARM64.Build.0 = Debug|ARM64
{F055103B-F80B-4D0C-BF48-057C55620033}.Debug|x64.ActiveCfg = Debug|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Debug|x64.Build.0 = Debug|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Debug|x86.ActiveCfg = Debug|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Debug|x86.Build.0 = Debug|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|Any CPU.ActiveCfg = Release|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|Any CPU.Build.0 = Release|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|ARM64.ActiveCfg = Release|ARM64
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|ARM64.Build.0 = Release|ARM64
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|x64.ActiveCfg = Release|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|x64.Build.0 = Release|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|x86.ActiveCfg = Release|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|x86.Build.0 = Release|x64
{6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Debug|Any CPU.ActiveCfg = Debug|x64
{6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Debug|Any CPU.Build.0 = Debug|x64
{6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Debug|ARM64.ActiveCfg = Debug|ARM64
{6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Debug|ARM64.Build.0 = Debug|ARM64
{6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Debug|x64.ActiveCfg = Debug|x64
{6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Debug|x64.Build.0 = Debug|x64
{6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Debug|x86.ActiveCfg = Debug|x64
{6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Debug|x86.Build.0 = Debug|x64
{6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Release|Any CPU.ActiveCfg = Release|x64
{6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Release|Any CPU.Build.0 = Release|x64
{6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Release|ARM64.ActiveCfg = Release|ARM64
{6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Release|ARM64.Build.0 = Release|ARM64
{6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Release|x64.ActiveCfg = Release|x64
{6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Release|x64.Build.0 = Release|x64
{6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Release|x86.ActiveCfg = Release|x64
{6CE421AD-D249-4BD1-9ADA-B46DA18AADEE}.Release|x86.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BE6AD818-2650-419C-8FDE-535C22ED09B3}
EndGlobalSection
EndGlobal

View File

@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2"/>
</startup>
<runtime>
<AppContextSwitchOverrides value = "Switch.System.Windows.DoNotScaleForDpiChanges=false"/>
</runtime>
</configuration>

View File

@@ -1,20 +0,0 @@
<Application
x:Class="ProjectsEditor.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ProjectsEditor"
xmlns:ui="http://schemas.modernwpf.com/2019"
Exit="OnExit"
Startup="OnStartup">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ui:ThemeResources />
<ui:XamlControlsResources />
<ResourceDictionary Source="pack://application:,,,/Styles/ButtonStyles.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style x:Key="HeadingTextBlock" TargetType="TextBlock" />
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@@ -1,109 +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.Windows;
using ManagedCommon;
using ProjectsEditor.Common;
using ProjectsEditor.Utils;
using ProjectsEditor.ViewModels;
namespace ProjectsEditor
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application, IDisposable
{
public static ProjectsEditorIO ProjectsEditorIO { get; private set; }
private MainWindow _mainWindow;
private MainViewModel _mainViewModel;
private ThemeManager _themeManager;
private bool _isDisposed;
public App()
{
ProjectsEditorIO = new ProjectsEditorIO();
}
private void OnStartup(object sender, StartupEventArgs e)
{
if (PowerToys.GPOWrapperProjection.GPOWrapper.GetConfiguredProjectsEnabledValue() == PowerToys.GPOWrapperProjection.GpoRuleConfigured.Disabled)
{
Logger.LogWarning("Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator.");
Shutdown(0);
return;
}
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
Logger.InitializeLogger("\\Projects\\Logs");
_themeManager = new ThemeManager(this);
if (_mainViewModel == null)
{
_mainViewModel = new MainViewModel(ProjectsEditorIO);
}
var parseResult = ProjectsEditorIO.ParseProjects(_mainViewModel);
string[] args = Environment.GetCommandLineArgs();
if (args != null && args.Length > 1)
{
Logger.LogInfo($"Started with a parameter: {args[1]}. Trying to launch that project.");
_mainViewModel.LaunchProject(args[1]);
return;
}
// normal start of editor
if (_mainWindow == null)
{
_mainWindow = new MainWindow(_mainViewModel);
}
// reset main window owner to keep it on the top
_mainWindow.ShowActivated = true;
_mainWindow.Topmost = true;
_mainWindow.Show();
// we can reset topmost flag after it's opened
_mainWindow.Topmost = false;
}
private void OnExit(object sender, ExitEventArgs e)
{
Dispose();
}
private void OnUnhandledException(object sender, UnhandledExceptionEventArgs args)
{
// TODO: log the error and show an error message
}
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
if (disposing)
{
_themeManager?.Dispose();
}
_isDisposed = true;
}
}
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}

View File

@@ -1,25 +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.Collections.Generic;
using ControlzEx.Theming;
namespace ProjectsEditor.Common
{
public class CustomLibraryThemeProvider : LibraryThemeProvider
{
public static readonly CustomLibraryThemeProvider DefaultInstance = new CustomLibraryThemeProvider();
public CustomLibraryThemeProvider()
: base(true)
{
}
/// <inheritdoc />
public override void FillColorSchemeValues(Dictionary<string, string> values, RuntimeThemeColorValues colorValues)
{
}
}
}

View File

@@ -1,23 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace ProjectsEditor.Common
{
public enum Theme
{
System,
Light,
Dark,
HighContrastOne,
HighContrastTwo,
HighContrastBlack,
HighContrastWhite,
}
public enum AppTheme
{
Dark = 0,
Light = 1,
}
}

View File

@@ -1,205 +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 ManagedCommon;
using System;
using System.Linq;
using System.Windows;
using Microsoft.Win32;
namespace ProjectsEditor.Common
{
public class ThemeManager : IDisposable
{
private readonly Application _app;
private const string LightTheme = "Light.Accent1";
private const string DarkTheme = "Dark.Accent1";
private const string HighContrastOneTheme = "HighContrast.Accent2";
private const string HighContrastTwoTheme = "HighContrast.Accent3";
private const string HighContrastBlackTheme = "HighContrast.Accent4";
private const string HighContrastWhiteTheme = "HighContrast.Accent5";
private static Theme _currentTheme;
private Theme _settingsTheme;
private bool _disposed;
public event ThemeChangedHandler ThemeChanged;
public ThemeManager(Application app)
{
_app = app;
Uri highContrastOneThemeUri = new Uri("pack://application:,,,/Themes/HighContrast1.xaml");
Uri highContrastTwoThemeUri = new Uri("pack://application:,,,/Themes/HighContrast2.xaml");
Uri highContrastBlackThemeUri = new Uri("pack://application:,,,/Themes/HighContrastWhite.xaml");
Uri highContrastWhiteThemeUri = new Uri("pack://application:,,,/Themes/HighContrastBlack.xaml");
Uri lightThemeUri = new Uri("pack://application:,,,/Themes/Light.xaml");
Uri darkThemeUri = new Uri("pack://application:,,,/Themes/Dark.xaml");
ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme(
new ControlzEx.Theming.LibraryTheme(
highContrastOneThemeUri,
CustomLibraryThemeProvider.DefaultInstance));
ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme(
new ControlzEx.Theming.LibraryTheme(
highContrastTwoThemeUri,
CustomLibraryThemeProvider.DefaultInstance));
ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme(
new ControlzEx.Theming.LibraryTheme(
highContrastBlackThemeUri,
CustomLibraryThemeProvider.DefaultInstance));
ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme(
new ControlzEx.Theming.LibraryTheme(
highContrastWhiteThemeUri,
CustomLibraryThemeProvider.DefaultInstance));
ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme(
new ControlzEx.Theming.LibraryTheme(
lightThemeUri,
CustomLibraryThemeProvider.DefaultInstance));
ControlzEx.Theming.ThemeManager.Current.AddLibraryTheme(
new ControlzEx.Theming.LibraryTheme(
darkThemeUri,
CustomLibraryThemeProvider.DefaultInstance));
ResetTheme();
ControlzEx.Theming.ThemeManager.Current.ThemeSyncMode = ControlzEx.Theming.ThemeSyncMode.SyncWithAppMode;
ControlzEx.Theming.ThemeManager.Current.ThemeChanged += Current_ThemeChanged;
SystemParameters.StaticPropertyChanged += SystemParameters_StaticPropertyChanged;
}
private void SystemParameters_StaticPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(SystemParameters.HighContrast))
{
ResetTheme();
}
}
public static Theme GetCurrentTheme()
{
return _currentTheme;
}
private static Theme GetHighContrastBaseType()
{
string registryKey = @"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes";
string theme = (string)Registry.GetValue(registryKey, "CurrentTheme", string.Empty);
theme = theme.Split('\\').Last().Split('.').First().ToString();
switch (theme)
{
case "hc1":
return Theme.HighContrastOne;
case "hc2":
return Theme.HighContrastTwo;
case "hcwhite":
return Theme.HighContrastWhite;
case "hcblack":
return Theme.HighContrastBlack;
default:
return Theme.HighContrastOne;
}
}
private void ResetTheme()
{
ChangeTheme(_settingsTheme == Theme.System ? Theme.System : _currentTheme);
}
public static string GetWindowsBaseColor()
{
return ControlzEx.Theming.WindowsThemeHelper.GetWindowsBaseColor();
}
public void ChangeTheme(Theme theme, bool fromSettings = false)
{
if (fromSettings)
{
_settingsTheme = theme;
}
Theme oldTheme = _currentTheme;
if (theme == Theme.System)
{
_currentTheme = Theme.System;
if (ControlzEx.Theming.WindowsThemeHelper.IsHighContrastEnabled())
{
Theme highContrastBaseType = GetHighContrastBaseType();
ChangeTheme(highContrastBaseType, false);
}
else
{
string baseColor = ControlzEx.Theming.WindowsThemeHelper.GetWindowsBaseColor();
ChangeTheme((Theme)Enum.Parse(typeof(Theme), baseColor));
}
}
else if (theme == Theme.HighContrastOne)
{
_currentTheme = Theme.HighContrastOne;
ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, HighContrastOneTheme);
}
else if (theme == Theme.HighContrastTwo)
{
_currentTheme = Theme.HighContrastTwo;
ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, HighContrastTwoTheme);
}
else if (theme == Theme.HighContrastWhite)
{
_currentTheme = Theme.HighContrastWhite;
ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, HighContrastWhiteTheme);
}
else if (theme == Theme.HighContrastBlack)
{
_currentTheme = Theme.HighContrastBlack;
ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, HighContrastBlackTheme);
}
else if (theme == Theme.Light)
{
_currentTheme = Theme.Light;
ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, LightTheme);
}
else if (theme == Theme.Dark)
{
_currentTheme = Theme.Dark;
ControlzEx.Theming.ThemeManager.Current.ChangeTheme(_app, DarkTheme);
}
ThemeChanged?.Invoke(oldTheme, _currentTheme);
}
private void Current_ThemeChanged(object sender, ControlzEx.Theming.ThemeChangedEventArgs e)
{
ControlzEx.Theming.ThemeManager.Current.ThemeChanged -= Current_ThemeChanged;
try
{
ResetTheme();
}
finally
{
ControlzEx.Theming.ThemeManager.Current.ThemeChanged += Current_ThemeChanged;
}
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
ControlzEx.Theming.ThemeManager.Current.ThemeChanged -= Current_ThemeChanged;
_disposed = true;
}
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
public delegate void ThemeChangedHandler(Theme oldTheme, Theme newTheme);
}

View File

@@ -1,29 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace ProjectsEditor.Converters
{
public class BooleanToInvertedVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((bool)value)
{
return Visibility.Collapsed;
}
return Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -1,110 +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.Collections.Generic;
using Projects.Data;
using ProjectsEditor.Utils;
using static ProjectsEditor.Data.ProjectsData;
namespace ProjectsEditor.Data
{
public class ProjectsData : ProjectsEditorData<ProjectsListWrapper>
{
public string File
{
get
{
return FolderUtils.DataFolder() + "\\projects.json";
}
}
public struct ApplicationWrapper
{
public struct WindowPositionWrapper
{
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
}
public string Application { get; set; }
public string ApplicationPath { get; set; }
public string Title { get; set; }
public string PackageFullName { get; set; }
public string CommandLineArguments { get; set; }
public bool Minimized { get; set; }
public bool Maximized { get; set; }
public WindowPositionWrapper Position { get; set; }
public int Monitor { get; set; }
}
public struct MonitorConfigurationWrapper
{
public struct MonitorRectWrapper
{
public int Top { get; set; }
public int Left { get; set; }
public int Width { get; set; }
public int Height { get; set; }
}
public string Id { get; set; }
public string InstanceId { get; set; }
public int MonitorNumber { get; set; }
public int Dpi { get; set; }
public MonitorRectWrapper MonitorRectDpiAware { get; set; }
public MonitorRectWrapper MonitorRectDpiUnaware { get; set; }
}
public struct ProjectWrapper
{
public string Id { get; set; }
public string Name { get; set; }
public long CreationTime { get; set; }
public long LastLaunchedTime { get; set; }
public bool IsShortcutNeeded { get; set; }
public List<MonitorConfigurationWrapper> MonitorConfiguration { get; set; }
public List<ApplicationWrapper> Applications { get; set; }
}
public struct ProjectsListWrapper
{
public List<ProjectWrapper> Projects { get; set; }
}
public enum OrderBy
{
LastViewed = 0,
Created = 1,
Name = 2,
Unknown = 3,
}
}
}

View File

@@ -1,36 +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.Text.Json;
using ProjectsEditor.Utils;
namespace Projects.Data
{
public class ProjectsEditorData<T>
{
protected JsonSerializerOptions JsonOptions
{
get
{
return new JsonSerializerOptions
{
PropertyNamingPolicy = new DashCaseNamingPolicy(),
WriteIndented = true,
};
}
}
public T Read(string file)
{
IOUtils ioUtils = new IOUtils();
string data = ioUtils.ReadFile(file);
return JsonSerializer.Deserialize<T>(data, JsonOptions);
}
public string Serialize(T data)
{
return JsonSerializer.Serialize(data, JsonOptions);
}
}
}

View File

@@ -1,31 +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.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Controls;
namespace ProjectsEditor
{
public class HeadingTextBlock : TextBlock
{
protected override AutomationPeer OnCreateAutomationPeer()
{
return new HeadingTextBlockAutomationPeer(this);
}
internal sealed class HeadingTextBlockAutomationPeer : TextBlockAutomationPeer
{
public HeadingTextBlockAutomationPeer(HeadingTextBlock owner)
: base(owner)
{
}
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.Header;
}
}
}
}

View File

@@ -1,322 +0,0 @@
<Page
x:Class="ProjectsEditor.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:props="clr-namespace:ProjectsEditor.Properties"
xmlns:converters="clr-namespace:ProjectsEditor.Converters"
xmlns:local="clr-namespace:ProjectsEditor"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="MainPage">
<Page.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis" />
<converters:BooleanToInvertedVisibilityConverter x:Key="BooleanToInvertedVisibilityConverter" />
<Thickness x:Key="ContentDialogPadding">24,16,0,24</Thickness>
<Thickness x:Key="ContentDialogCommandSpaceMargin">0,24,24,0</Thickness>
<Style x:Key="DeleteButtonStyle" TargetType="Button">
<Setter Property="Background" Value="{DynamicResource TertiaryBackgroundBrush}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border" Background="{TemplateBinding Background}" BorderBrush="Transparent" Padding="26,6,26,6">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="Background" Value="{DynamicResource TitleBarSecondaryForegroundBrush}" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="border" Property="Background" Value="{DynamicResource TitleBarSecondaryForegroundBrush}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<local:HeadingTextBlock
x:Name="ProjectsHeaderBlock"
AutomationProperties.HeadingLevel="Level1"
FontSize="24"
FontWeight="SemiBold"
Text="{x:Static props:Resources.Projects}"
Foreground="{DynamicResource PrimaryForegroundBrush}"
Grid.Row="0"
Margin="40,20,40,20"/>
<Button
x:Name="NewProjectButton"
Height="36"
Padding="0"
Grid.Row="0"
Margin="0,20,40,20"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
AutomationProperties.Name="{x:Static props:Resources.CreateProject}"
Click="NewProjectButton_Click"
Style="{StaticResource AccentButtonStyle}"
TabIndex="3">
<StackPanel Margin="12, 8, 12, 8" Orientation="Horizontal">
<TextBlock
AutomationProperties.Name="{x:Static props:Resources.CreateProject}"
FontFamily="{DynamicResource SymbolThemeFontFamily}"
Foreground="{DynamicResource AccentButtonForeground}"
Text="&#xE710;" />
<TextBlock
Margin="12,-3,0,0"
Foreground="{DynamicResource AccentButtonForeground}"
Text="{x:Static props:Resources.CreateProject}" />
</StackPanel>
<Button.Effect>
<DropShadowEffect
BlurRadius="6"
Opacity="0.32"
ShadowDepth="1" />
</Button.Effect>
</Button>
<Border
HorizontalAlignment="Left"
Grid.Row="1"
Margin="40,0,0,0"
BorderThickness="2"
VerticalAlignment="Center"
CornerRadius="5">
<StackPanel
Orientation="Horizontal">
<Grid>
<TextBox
x:Name="SearchTextBox"
Width="320"
Text="{Binding SearchTerm, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Background="{DynamicResource SecondaryBackgroundBrush}"
BorderBrush="{DynamicResource PrimaryBorderBrush}"
ToolTip="{x:Static props:Resources.SearchExplanation}"
/>
<TextBlock
IsHitTestVisible="False"
Text="{x:Static props:Resources.Search}"
VerticalAlignment="Center"
Foreground="{DynamicResource SecondaryForegroundBrush}"
ToolTip="{x:Static props:Resources.SearchExplanation}"
Margin="10,0,0,0">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=SearchTextBox}" Value="">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
<TextBlock
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="-50,0,34,0"
AutomationProperties.Name="{x:Static props:Resources.Search}"
FontFamily="{DynamicResource SymbolThemeFontFamily}"
Foreground="{DynamicResource SecondaryForegroundBrush}"
Text="&#xE71E;" />
</StackPanel>
</Border>
<StackPanel
Grid.Row="1"
HorizontalAlignment="Right"
Orientation="Horizontal"
Margin="0,0,40,0">
<TextBlock
VerticalAlignment="Center"
Margin="10,0,10,0"
Text="{x:Static props:Resources.SortBy}"
Foreground="{DynamicResource PrimaryForegroundBrush}"
/>
<ComboBox
Width="140"
Background="{DynamicResource SecondaryBackgroundBrush}"
BorderBrush="{DynamicResource PrimaryBorderBrush}"
SelectedIndex="{Binding OrderByIndex, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ComboBoxItem Content="{x:Static props:Resources.LastLaunched}" />
<ComboBoxItem Content="{x:Static props:Resources.Created}" />
<ComboBoxItem Content="{x:Static props:Resources.Name}" />
</ComboBox>
</StackPanel>
<TextBlock
Grid.Row="2"
Text="{Binding EmptyProjectsViewMessage, UpdateSourceTrigger=PropertyChanged}"
FontSize="20"
Foreground="{DynamicResource SecondaryForegroundBrush}"
TextAlignment="Center"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Visibility="{Binding IsProjectsViewEmpty, Mode=OneWay, Converter={StaticResource BoolToVis}, UpdateSourceTrigger=PropertyChanged}"/>
<ScrollViewer
VerticalContentAlignment="Stretch"
VerticalScrollBarVisibility="Auto"
Grid.Row="2"
Margin="40,15,40,40"
Visibility="{Binding IsProjectsViewEmpty, Mode=OneWay, Converter={StaticResource BooleanToInvertedVisibilityConverter}, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl ItemsSource="{Binding ProjectsView, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel
IsItemsHost="True"
Orientation="Vertical"
HorizontalAlignment="Stretch"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="models:Project">
<Button
x:Name="EditButton"
AutomationProperties.Name="{x:Static props:Resources.Edit}"
Margin="0,12,0,0"
Click="EditButtonClicked"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
Background="{DynamicResource SecondaryBackgroundBrush}"
Padding="1">
<Border Background="{DynamicResource SecondaryBackgroundBrush}"
HorizontalAlignment="Stretch"
CornerRadius="5">
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="110" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Vertical" HorizontalAlignment="Left" Margin="12,14,10,10">
<TextBlock
Text="{Binding Name, Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"
FontSize="16"
Margin="0,0,0,8"
FontWeight="SemiBold"
HorizontalAlignment="Left"
VerticalAlignment="Center"/>
<StackPanel
Orientation="Horizontal"
VerticalAlignment="Center"
Margin="0,0,0,8" >
<Image
Source="{Binding PreviewIcons, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Height="20" />
<TextBlock
Text="{Binding AppsCountString}"
Margin="6,0,4,0"
VerticalAlignment="Center"/>
</StackPanel>
<StackPanel
Orientation="Horizontal"
VerticalAlignment="Center">
<TextBlock
FontFamily="{DynamicResource SymbolThemeFontFamily}"
Foreground="{DynamicResource PrimaryForegroundBrush}"
Text="&#xE81C;"
Margin="0,3,10,0"/>
<TextBlock Text="{Binding LastLaunched, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</StackPanel>
<StackPanel Orientation="Vertical" Grid.Column="1" Margin="12,12,12,12">
<StackPanel
Orientation="Horizontal"
HorizontalAlignment="Right">
<Button
x:Name="MoreButton"
HorizontalAlignment="Right"
Style="{StaticResource IconOnlyButtonStyle}"
Click="MoreButton_Click">
<TextBlock
FontFamily="{DynamicResource SymbolThemeFontFamily}"
Foreground="{DynamicResource PrimaryForegroundBrush}"
Text="&#xE712;"/>
</Button>
<Popup
IsOpen="{Binding IsPopupVisible, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
StaysOpen="False"
AllowsTransparency="True"
PlacementTarget="{Binding ElementName=MoreButton}"
Placement="Left">
<Grid
Background="{DynamicResource PrimaryBackgroundBrush}">
<Grid.OpacityMask>
<VisualBrush Visual="{Binding ElementName=OpacityBorder}" />
</Grid.OpacityMask>
<Border
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
x:Name="OpacityBorder"
Background="Black"
CornerRadius="5" />
<StackPanel
Background="{DynamicResource PrimaryBackgroundBrush}"
Orientation="Vertical">
<Button
Style="{StaticResource DeleteButtonStyle}"
AutomationProperties.Name="{x:Static props:Resources.Edit}"
Click="EditButtonClicked">
<StackPanel Orientation="Horizontal">
<TextBlock
AutomationProperties.Name="{x:Static props:Resources.Edit}"
FontFamily="{DynamicResource SymbolThemeFontFamily}"
Foreground="{DynamicResource PrimaryForegroundBrush}"
Text="&#xE70F;" />
<TextBlock
Margin="10,0,0,0"
AutomationProperties.Name="{x:Static props:Resources.Edit}"
Foreground="{DynamicResource PrimaryForegroundBrush}"
Text="{x:Static props:Resources.Edit}" />
</StackPanel>
</Button>
<Button
Style="{StaticResource DeleteButtonStyle}"
AutomationProperties.Name="{x:Static props:Resources.Delete}"
Click="DeleteButtonClicked">
<StackPanel Orientation="Horizontal">
<TextBlock
AutomationProperties.Name="{x:Static props:Resources.Delete}"
FontFamily="{DynamicResource SymbolThemeFontFamily}"
Foreground="{DynamicResource PrimaryForegroundBrush}"
Text="&#xE74D;" />
<TextBlock
Margin="10,0,0,0"
AutomationProperties.Name="{x:Static props:Resources.Delete}"
Foreground="{DynamicResource PrimaryForegroundBrush}"
Text="{x:Static props:Resources.Delete}" />
</StackPanel>
</Button>
</StackPanel>
</Grid>
</Popup>
</StackPanel>
<Button
Padding="20,4,20,4"
Margin="0,6,0,0"
AutomationProperties.Name="{x:Static props:Resources.Launch}"
Content="{x:Static props:Resources.Launch}"
HorizontalAlignment="Right"
Background="{DynamicResource TertiaryBackgroundBrush}"
BorderBrush="{DynamicResource SecondaryBorderBrush}"
BorderThickness="1"
Click="LaunchButton_Click"/>
</StackPanel>
</Grid>
</Border>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</Page>

View File

@@ -1,63 +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.Windows;
using System.Windows.Controls;
using ProjectsEditor.Models;
using ProjectsEditor.ViewModels;
namespace ProjectsEditor
{
/// <summary>
/// Interaction logic for MainPage.xaml
/// </summary>
public partial class MainPage : Page
{
private MainViewModel _mainViewModel;
public MainPage(MainViewModel mainViewModel)
{
InitializeComponent();
_mainViewModel = mainViewModel;
this.DataContext = _mainViewModel;
}
private /*async*/ void NewProjectButton_Click(object sender, RoutedEventArgs e)
{
_mainViewModel.AddNewProject();
}
private void EditButtonClicked(object sender, RoutedEventArgs e)
{
_mainViewModel.CloseAllPopups();
Button button = sender as Button;
Project selectedProject = button.DataContext as Project;
_mainViewModel.EditProject(selectedProject);
}
private void DeleteButtonClicked(object sender, RoutedEventArgs e)
{
e.Handled = true;
Button button = sender as Button;
Project selectedProject = button.DataContext as Project;
_mainViewModel.DeleteProject(selectedProject);
}
private void MoreButton_Click(object sender, RoutedEventArgs e)
{
e.Handled = true;
Button button = sender as Button;
Project project = button.DataContext as Project;
project.IsPopupVisible = true;
}
private void LaunchButton_Click(object sender, RoutedEventArgs e)
{
e.Handled = true;
Button button = sender as Button;
Project project = button.DataContext as Project;
_mainViewModel.LaunchProject(project);
}
}
}

View File

@@ -1,33 +0,0 @@
<Window
x:Class="ProjectsEditor.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:props="clr-namespace:ProjectsEditor.Properties"
xmlns:ui="http://schemas.modernwpf.com/2019"
x:Name="ProjectsMainWindow"
Title="{x:Static props:Resources.MainTitle}"
ui:TitleBar.Background="{DynamicResource PrimaryBackgroundBrush}"
ui:TitleBar.InactiveBackground="{DynamicResource TertiaryBackgroundBrush}"
ui:TitleBar.IsIconVisible="True"
ui:WindowHelper.UseModernWindowStyle="True"
MinWidth="700"
MinHeight="680"
AutomationProperties.Name="Projects Editor"
Closing="OnClosing"
ContentRendered="OnContentRendered"
ResizeMode="CanResize"
WindowStartupLocation="CenterOwner"
mc:Ignorable="d"
Background="{DynamicResource PrimaryBackgroundBrush}">
<Border
CornerRadius="20"
BorderThickness="1">
<Grid Margin="0,10,0,0">
<Frame
x:Name="ContentFrame"
NavigationUIVisibility="Hidden"/>
</Grid>
</Border>
</Window>

View File

@@ -1,100 +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.Windows;
using System.Windows.Interop;
using ProjectsEditor.Utils;
using ProjectsEditor.ViewModels;
namespace ProjectsEditor
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private bool haveTriedToGetFocusAlready;
public MainViewModel MainViewModel { get; set; }
private static MainPage _mainPage;
public MainWindow(MainViewModel mainViewModel)
{
MainViewModel = mainViewModel;
mainViewModel.SetMainWindow(this);
InitializeComponent();
_mainPage = new MainPage(mainViewModel);
ContentFrame.Navigate(_mainPage);
MaxWidth = SystemParameters.PrimaryScreenWidth;
MaxHeight = SystemParameters.PrimaryScreenHeight;
}
private void BringToFront()
{
// Get the window handle of the Projects Editor window
IntPtr handle = new WindowInteropHelper(this).Handle;
// Get the handle of the window currently in the foreground
IntPtr foregroundWindowHandle = NativeMethods.GetForegroundWindow();
// Get the thread IDs of the current thread and the thread of the foreground window
uint currentThreadId = NativeMethods.GetCurrentThreadId();
uint activeThreadId = NativeMethods.GetWindowThreadProcessId(foregroundWindowHandle, IntPtr.Zero);
// Check if the active thread is different from the current thread
if (activeThreadId != currentThreadId)
{
// Attach the input processing mechanism of the current thread to the active thread
NativeMethods.AttachThreadInput(activeThreadId, currentThreadId, true);
// Set the Projects Editor window as the foreground window
NativeMethods.SetForegroundWindow(handle);
// Detach the input processing mechanism of the current thread from the active thread
NativeMethods.AttachThreadInput(activeThreadId, currentThreadId, false);
}
else
{
// Set the Projects Editor window as the foreground window
NativeMethods.SetForegroundWindow(handle);
}
// Bring the Projects Editor window to the foreground and activate it
NativeMethods.SwitchToThisWindow(handle, true);
haveTriedToGetFocusAlready = true;
}
private void OnClosing(object sender, EventArgs e)
{
App.Current.Shutdown();
}
// This is required to fix a WPF rendering bug when using custom chrome
private void OnContentRendered(object sender, EventArgs e)
{
if (!haveTriedToGetFocusAlready)
{
BringToFront();
}
InvalidateVisual();
}
public void ShowPage(ProjectEditor editPage)
{
ContentFrame.Navigate(editPage);
}
public void SwitchToMainView()
{
ContentFrame.GoBack();
}
}
}

View File

@@ -1,31 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace ProjectsEditor.Models
{
public sealed class AppListDataTemplateSelector : System.Windows.Controls.DataTemplateSelector
{
public System.Windows.DataTemplate HeaderTemplate { get; set; }
public System.Windows.DataTemplate AppTemplate { get; set; }
public AppListDataTemplateSelector()
{
HeaderTemplate = new System.Windows.DataTemplate();
AppTemplate = new System.Windows.DataTemplate();
}
public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
{
if (item is string)
{
return HeaderTemplate;
}
else
{
return AppTemplate;
}
}
}
}

View File

@@ -1,298 +0,0 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;
using ManagedCommon;
using Windows.ApplicationModel.Core;
using Windows.Management.Deployment;
namespace ProjectsEditor.Models
{
public class Application : INotifyPropertyChanged, IDisposable
{
public event PropertyChangedEventHandler PropertyChanged;
public Project Parent { get; set; }
public struct WindowPosition
{
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
}
public string AppName { get; set; }
public string AppPath { get; set; }
public string AppTitle { get; set; }
public string PackageFullName { get; set; }
public string CommandLineArguments { get; set; }
public bool Minimized { get; set; }
public bool Maximized { get; set; }
private bool _isNotFound;
[JsonIgnore]
public bool IsNotFound
{
get
{
return _isNotFound;
}
set
{
if (_isNotFound != value)
{
_isNotFound = value;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsNotFound)));
}
}
}
[JsonIgnore]
public bool IsSelected { get; set; }
[JsonIgnore]
public bool IsHighlighted { get; set; }
[JsonIgnore]
public int RepeatIndex { get; set; }
[JsonIgnore]
public string RepeatIndexString
{
get
{
return RepeatIndex == 0 ? string.Empty : RepeatIndex.ToString(CultureInfo.InvariantCulture);
}
}
[JsonIgnore]
private Icon _icon = null;
[JsonIgnore]
public Icon Icon
{
get
{
if (_icon == null)
{
try
{
if (!File.Exists(AppPath) && IsPackagedApp)
{
Task<AppListEntry> task = Task.Run<AppListEntry>(async () => await GetAppByPackageFamilyNameAsync());
AppListEntry packApp = task.Result;
if (packApp == null)
{
IsNotFound = true;
_icon = new Icon(@"images\DefaultIcon.ico");
}
else
{
string filename = Path.GetFileName(AppPath);
string newExeLocation = Path.Combine(packApp.AppInfo.Package.InstalledPath, filename);
_icon = Icon.ExtractAssociatedIcon(newExeLocation);
}
}
else
{
_icon = Icon.ExtractAssociatedIcon(AppPath);
}
}
catch (Exception)
{
Logger.LogWarning($"Icon not found on app path: {AppPath}. Using default icon");
IsNotFound = true;
_icon = new Icon(@"images\DefaultIcon.ico");
}
}
return _icon;
}
}
public async Task<AppListEntry> GetAppByPackageFamilyNameAsync()
{
var pkgManager = new PackageManager();
var pkg = pkgManager.FindPackagesForUser(string.Empty, PackagedId).FirstOrDefault();
if (pkg == null)
{
return null;
}
var apps = await pkg.GetAppListEntriesAsync();
if (apps == null || apps.Count == 0)
{
return null;
}
AppListEntry firstApp = apps[0];
// RandomAccessStreamReference stream = firstApp.AppInfo.DisplayInfo.GetLogo(new Windows.Foundation.Size(64, 64));
// IRandomAccessStreamWithContentType content = await stream.OpenReadAsync();
// BitmapImage bitmapImage = new BitmapImage();
// bitmapImage.StreamSource = (Stream)content;
return firstApp;
}
private BitmapImage _iconBitmapImage;
public BitmapImage IconBitmapImage
{
get
{
if (_iconBitmapImage == null)
{
try
{
Bitmap previewBitmap = new Bitmap(32, 32);
using (Graphics graphics = Graphics.FromImage(previewBitmap))
{
graphics.SmoothingMode = SmoothingMode.AntiAlias;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.DrawIcon(Icon, new Rectangle(0, 0, 32, 32));
}
using (var memory = new MemoryStream())
{
previewBitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.Freeze();
_iconBitmapImage = bitmapImage;
}
}
catch (Exception e)
{
Logger.LogError($"Exception while drawing icon for app with path: {AppPath}. Exception message: {e.Message}");
}
}
return _iconBitmapImage;
}
}
public WindowPosition Position { get; set; }
private WindowPosition? _scaledPosition;
public WindowPosition ScaledPosition
{
get
{
if (_scaledPosition == null)
{
double scaleFactor = MonitorSetup.Dpi / 96.0;
_scaledPosition = new WindowPosition()
{
X = (int)(scaleFactor * Position.X),
Y = (int)(scaleFactor * Position.Y),
Height = (int)(scaleFactor * Position.Height),
Width = (int)(scaleFactor * Position.Width),
};
}
return _scaledPosition.Value;
}
}
public int MonitorNumber { get; set; }
private MonitorSetup _monitorSetup;
public MonitorSetup MonitorSetup
{
get
{
if (_monitorSetup == null)
{
_monitorSetup = Parent.Monitors.Where(x => x.MonitorNumber == MonitorNumber).FirstOrDefault();
}
return _monitorSetup;
}
}
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
private bool? _isPackagedApp;
public string PackagedId { get; set; }
public string PackagedName { get; set; }
public string PackagedPublisherID { get; set; }
public string Aumid { get; set; }
public bool IsPackagedApp
{
get
{
if (_isPackagedApp == null)
{
if (!AppPath.StartsWith("C:\\Program Files\\WindowsApps\\", StringComparison.InvariantCultureIgnoreCase))
{
_isPackagedApp = false;
}
else
{
string appPath = AppPath.Replace("C:\\Program Files\\WindowsApps\\", string.Empty);
Regex packagedAppPathRegex = new Regex(@"(?<APPID>[^_]*)_\d+.\d+.\d+.\d+_x64__(?<PublisherID>[^\\]*)", RegexOptions.ExplicitCapture | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
Match match = packagedAppPathRegex.Match(appPath);
_isPackagedApp = match.Success;
if (match.Success)
{
PackagedName = match.Groups["APPID"].Value;
PackagedPublisherID = match.Groups["PublisherID"].Value;
PackagedId = $"{PackagedName}_{PackagedPublisherID}";
Aumid = $"{PackagedId}!App";
}
}
}
return _isPackagedApp.Value;
}
}
public void Dispose()
{
GC.SuppressFinalize(this);
}
}
}

View File

@@ -1,33 +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.Windows;
namespace ProjectsEditor.Models
{
public class Monitor
{
public string MonitorName { get; private set; }
public string MonitorInstanceId { get; private set; }
public int MonitorNumber { get; private set; }
public int Dpi { get; private set; }
public Rect MonitorDpiUnawareBounds { get; private set; }
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

@@ -1,29 +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.ComponentModel;
using System.Windows;
using System.Windows.Media.Imaging;
namespace ProjectsEditor.Models
{
public class MonitorSetup : Monitor, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
public string MonitorInfo { get => MonitorName; }
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

@@ -1,307 +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.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.Threading.Tasks;
using System.Windows.Media.Imaging;
using ManagedCommon;
using ProjectsEditor.Utils;
namespace ProjectsEditor.Models
{
public class Project : INotifyPropertyChanged
{
[JsonIgnore]
public string EditorWindowTitle { get; set; }
public string Id { get; set; }
private string _name;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(Name)));
OnPropertyChanged(new PropertyChangedEventArgs(nameof(CanBeSaved)));
}
}
public long CreationTime { get; set; } // in seconds
public long LastLaunchedTime { get; set; } // in seconds
public bool IsShortcutNeeded { get; set; }
public string LastLaunched
{
get
{
string lastLaunched = ProjectsEditor.Properties.Resources.LastLaunched + ": ";
if (LastLaunchedTime == 0)
{
return lastLaunched + ProjectsEditor.Properties.Resources.Never;
}
const int SECOND = 1;
const int MINUTE = 60 * SECOND;
const int HOUR = 60 * MINUTE;
const int DAY = 24 * HOUR;
const int MONTH = 30 * DAY;
DateTime lastLaunchDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(LastLaunchedTime);
var now = DateTime.UtcNow.Ticks;
var ts = DateTime.UtcNow - lastLaunchDateTime;
double delta = Math.Abs(ts.TotalSeconds);
if (delta < 1 * MINUTE)
{
return lastLaunched + ProjectsEditor.Properties.Resources.Recently;
}
if (delta < 2 * MINUTE)
{
return lastLaunched + ProjectsEditor.Properties.Resources.OneMinuteAgo;
}
if (delta < 45 * MINUTE)
{
return lastLaunched + ts.Minutes + " " + ProjectsEditor.Properties.Resources.MinutesAgo;
}
if (delta < 90 * MINUTE)
{
return lastLaunched + ProjectsEditor.Properties.Resources.OneHourAgo;
}
if (delta < 24 * HOUR)
{
return lastLaunched + ts.Hours + " " + ProjectsEditor.Properties.Resources.HoursAgo;
}
if (delta < 48 * HOUR)
{
return lastLaunched + ProjectsEditor.Properties.Resources.Yesterday;
}
if (delta < 30 * DAY)
{
return lastLaunched + ts.Days + " " + ProjectsEditor.Properties.Resources.DaysAgo;
}
if (delta < 12 * MONTH)
{
int months = Convert.ToInt32(Math.Floor((double)ts.Days / 30));
return lastLaunched + (months <= 1 ? ProjectsEditor.Properties.Resources.OneMonthAgo : months + " " + ProjectsEditor.Properties.Resources.MonthsAgo);
}
else
{
int years = Convert.ToInt32(Math.Floor((double)ts.Days / 365));
return lastLaunched + (years <= 1 ? ProjectsEditor.Properties.Resources.OneYearAgo : years + " " + ProjectsEditor.Properties.Resources.YearsAgo);
}
}
}
public bool CanBeSaved
{
get => Name.Length > 0 && Applications.Where(x => x.IsSelected).Any();
}
private bool _isPopupVisible;
[JsonIgnore]
public bool IsPopupVisible
{
get
{
return _isPopupVisible;
}
set
{
_isPopupVisible = value;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsPopupVisible)));
}
}
public List<Application> Applications { get; set; }
public List<object> ApplicationsListed
{
get
{
List<object> applicationsListed = new List<object>();
ILookup<MonitorSetup, Application> apps = Applications.Where(x => !x.Minimized).ToLookup(x => x.MonitorSetup);
foreach (var appItem in apps.OrderBy(x => x.Key.MonitorDpiUnawareBounds.Left).ThenBy(x => x.Key.MonitorDpiUnawareBounds.Top))
{
applicationsListed.Add(appItem.Key.MonitorInfo);
foreach (Application app in appItem)
{
applicationsListed.Add(app);
}
}
var minimizedApps = Applications.Where(x => x.Minimized);
if (minimizedApps.Any())
{
applicationsListed.Add("Minimized Apps");
foreach (Application app in minimizedApps)
{
applicationsListed.Add(app);
}
}
return applicationsListed;
}
}
[JsonIgnore]
public string AppsCountString
{
get
{
int count = Applications.Where(x => x.IsSelected).Count();
return count.ToString(CultureInfo.InvariantCulture) + " " + (count == 1 ? Properties.Resources.App : Properties.Resources.Apps);
}
}
public List<MonitorSetup> Monitors { get; set; }
private BitmapImage _previewIcons;
private BitmapImage _previewImage;
private double _previewImageWidth;
public Project(Project selectedProject)
{
Name = selectedProject.Name;
PreviewIcons = selectedProject.PreviewIcons;
PreviewImage = selectedProject.PreviewImage;
IsShortcutNeeded = selectedProject.IsShortcutNeeded;
int screenIndex = 1;
Monitors = new List<MonitorSetup>();
foreach (var item in selectedProject.Monitors.OrderBy(x => x.MonitorDpiAwareBounds.Left).ThenBy(x => x.MonitorDpiAwareBounds.Top))
{
Monitors.Add(new MonitorSetup($"Screen {screenIndex}", item.MonitorInstanceId, item.MonitorNumber, item.Dpi, item.MonitorDpiAwareBounds, item.MonitorDpiUnawareBounds));
screenIndex++;
}
Applications = new List<Application>();
foreach (var item in selectedProject.Applications)
{
Applications.Add(new Application()
{
AppName = item.AppName,
AppPath = item.AppPath,
AppTitle = item.AppTitle,
CommandLineArguments = item.CommandLineArguments,
PackageFullName = item.PackageFullName,
Minimized = item.Minimized,
Maximized = item.Maximized,
IsSelected = item.IsSelected,
MonitorNumber = item.MonitorNumber,
IsNotFound = item.IsNotFound,
Position = new Application.WindowPosition() { X = item.Position.X, Y = item.Position.Y, Height = item.Position.Height, Width = item.Position.Width },
Parent = this,
});
}
}
public Project()
{
}
public BitmapImage PreviewIcons
{
get
{
return _previewIcons;
}
set
{
_previewIcons = value;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(PreviewIcons)));
}
}
public BitmapImage PreviewImage
{
get
{
return _previewImage;
}
set
{
_previewImage = value;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(PreviewImage)));
}
}
public double PreviewImageWidth
{
get
{
return _previewImageWidth;
}
set
{
_previewImageWidth = value;
OnPropertyChanged(new PropertyChangedEventArgs(nameof(PreviewImageWidth)));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChanged?.Invoke(this, e);
}
public async void Initialize()
{
PreviewIcons = await Task.Run(() => DrawHelper.DrawPreviewIcons(this));
Rectangle commonBounds = GetCommonBounds();
PreviewImage = await Task.Run(() => DrawHelper.DrawPreview(this, commonBounds));
PreviewImageWidth = commonBounds.Width / (commonBounds.Height * 1.2 / 200);
}
private Rectangle GetCommonBounds()
{
double minX = Monitors.First().MonitorDpiAwareBounds.Left;
double minY = Monitors.First().MonitorDpiAwareBounds.Top;
double maxX = Monitors.First().MonitorDpiAwareBounds.Right;
double maxY = Monitors.First().MonitorDpiAwareBounds.Bottom;
for (int monitorIndex = 1; monitorIndex < Monitors.Count; monitorIndex++)
{
Monitor monitor = Monitors[monitorIndex];
minX = Math.Min(minX, monitor.MonitorDpiAwareBounds.Left);
minY = Math.Min(minY, monitor.MonitorDpiAwareBounds.Top);
maxX = Math.Max(maxX, monitor.MonitorDpiAwareBounds.Right);
maxY = Math.Max(maxY, monitor.MonitorDpiAwareBounds.Bottom);
}
return new Rectangle((int)minX, (int)minY, (int)(maxX - minX), (int)(maxY - minY));
}
}
}

View File

@@ -1,233 +0,0 @@
<Page x:Class="ProjectsEditor.ProjectEditor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:props="clr-namespace:ProjectsEditor.Properties"
xmlns:local="clr-namespace:ProjectsEditor"
xmlns:models="clr-namespace:ProjectsEditor.Models"
mc:Ignorable="d"
Title="Project Editor"
Background="{DynamicResource PrimaryBackgroundBrush}">
<Page.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis" />
<DataTemplate x:Key="headerTemplate">
<Border>
<TextBlock
Text="{Binding .}"
Foreground="{DynamicResource PrimaryForegroundBrush}"
FontSize="14"
FontWeight="Normal"
Margin="0,20,20,5"
VerticalAlignment="Center"/>
</Border>
</DataTemplate>
<DataTemplate x:Key="appTemplate">
<Border
Background="{DynamicResource SecondaryBackgroundBrush}"
MouseEnter="AppBorder_MouseEnter"
MouseLeave="AppBorder_MouseLeave">
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock
Text="&#xE7BA;"
Foreground="#EED202"
FontSize="14"
FontFamily="{DynamicResource SymbolThemeFontFamily}"
FontWeight="Normal"
Margin="5 0 0 0"
ToolTip="{x:Static props:Resources.NotFoundTooltip}"
Visibility="{Binding IsNotFound, Converter={StaticResource BoolToVis}, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
VerticalAlignment="Center"/>
<Image
Grid.Column="1"
Width="20"
Height="20"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="10"
Source="{Binding IconBitmapImage}"/>
<TextBlock
Grid.Column="2"
Text="{Binding RepeatIndexString, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Foreground="{DynamicResource PrimaryForegroundBrush}"
FontSize="14"
FontWeight="Normal"
Width="20"
VerticalAlignment="Center"/>
<TextBlock
Grid.Column="3"
Text="{Binding AppName}"
Foreground="{DynamicResource PrimaryForegroundBrush}"
FontSize="14"
FontWeight="Normal"
VerticalAlignment="Center"/>
<TextBox
x:Name="CommandLineTextBox"
Grid.Column="4"
Text="{Binding CommandLineArguments, Mode=TwoWay}"
Foreground="{DynamicResource PrimaryForegroundBrush}"
Background="{DynamicResource TertiaryBackgroundBrush}"
BorderThickness="0"
FontSize="14"
FontWeight="Normal"
VerticalContentAlignment="Center" />
<TextBlock
Grid.Column="4"
IsHitTestVisible="False"
Text="{x:Static props:Resources.WriteArgs}"
Foreground="{DynamicResource SecondaryForegroundBrush}"
Background="{DynamicResource TertiaryBackgroundBrush}"
FontSize="14"
FontWeight="Normal"
VerticalAlignment="Center"
Margin="12,0,12,0">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=CommandLineTextBox}" Value="">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<CheckBox
Grid.Column="5"
IsChecked="{Binding IsSelected, Mode=TwoWay}"
Checked="CheckBox_Checked"
Unchecked="CheckBox_Checked"
Margin="10"/>
</Grid>
</Border>
</DataTemplate>
<models:AppListDataTemplateSelector
HeaderTemplate="{StaticResource headerTemplate}"
AppTemplate="{StaticResource appTemplate}"
x:Key="AppListDataTemplateSelector"/>
</Page.Resources>
<Grid Margin="40,0,40,40">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Orientation="Horizontal">
<Button
Margin="0,20,0,20"
Click="CancelButtonClicked"
Background="Transparent"
VerticalAlignment="Center">
<TextBlock
Text="{x:Static props:Resources.Projects}"
FontSize="24"
FontWeight="Normal"
Foreground="{DynamicResource PrimaryForegroundBrush}"
VerticalAlignment="Center"/>
</Button>
<TextBlock
FontFamily="{DynamicResource SymbolThemeFontFamily}"
Foreground="{DynamicResource PrimaryForegroundBrush}"
FontSize="16"
Margin="10,0,0,0"
Text="&#xE76C;"
VerticalAlignment="Center"/>
<TextBlock
Text="{Binding EditorWindowTitle}"
FontSize="24"
FontWeight="SemiBold"
Margin="10,0,0,0"
Foreground="{DynamicResource PrimaryForegroundBrush}"
VerticalAlignment="Center"/>
</StackPanel>
<StackPanel Grid.Row="1" Orientation="Vertical">
<TextBlock Text="{x:Static props:Resources.ProjectName}" FontSize="14" FontWeight="Normal" Foreground="{DynamicResource PrimaryForegroundBrush}"/>
<TextBox
x:Name="EditNameTextBox"
Width="320"
Text="{Binding Name, Mode=TwoWay}"
Background="{DynamicResource SecondaryBackgroundBrush}"
BorderBrush="{DynamicResource PrimaryBorderBrush}"
BorderThickness="2"
Margin="0,6,0,6"
HorizontalAlignment="Left"
GotFocus="EditNameTextBox_GotFocus"
TextChanged="EditNameTextBox_TextChanged"
KeyDown="EditNameTextBoxKeyDown" />
</StackPanel>
<Border
Grid.Row="2"
HorizontalAlignment="Stretch"
Background="{DynamicResource MonitorViewBackgroundBrush}"
CornerRadius="5">
<Image
Width="{Binding PreviewImageWidth, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Height="200"
Source="{Binding PreviewImage, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Stretch="Fill"
Margin="2"/>
</Border>
<ScrollViewer
Margin="0,10,0,0"
VerticalScrollBarVisibility="Auto"
Grid.Row="3">
<StackPanel Orientation="Vertical">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ItemsControl
ItemsSource="{Binding ApplicationsListed, Mode=OneWay}"
ItemTemplateSelector="{StaticResource AppListDataTemplateSelector}">
</ItemsControl>
</Grid>
</StackPanel>
</ScrollViewer>
<DockPanel Grid.Row="4" Margin="0,20,0,20">
<CheckBox
DockPanel.Dock="Left"
Content="{x:Static props:Resources.CreateShortcut}"
IsChecked="{Binding IsShortcutNeeded, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
FontSize="14"/>
<StackPanel
DockPanel.Dock="Right"
Orientation="Horizontal"
HorizontalAlignment="Right"
Margin="40,0,0,0">
<Button
x:Name="CancelButton"
Margin="20,0,0,0"
Height="36"
Padding="24,0,24,0"
Content="{x:Static props:Resources.Cancel}"
Background="{DynamicResource SecondaryBackgroundBrush}"
AutomationProperties.Name="{x:Static props:Resources.Cancel}"
Click="CancelButtonClicked">
</Button>
<Button
x:Name="SaveButton"
Margin="20,0,0,0"
Padding="24,0,24,0"
Height="36"
IsEnabled="{Binding CanBeSaved, UpdateSourceTrigger=PropertyChanged}"
Content="{x:Static props:Resources.Save_project}"
AutomationProperties.Name="{x:Static props:Resources.Save_project}"
Click="SaveButtonClicked"
Style="{StaticResource AccentButtonStyle}">
</Button>
</StackPanel>
</DockPanel>
</Grid>
</Page>

View File

@@ -1,95 +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.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using ProjectsEditor.Models;
using ProjectsEditor.ViewModels;
namespace ProjectsEditor
{
/// <summary>
/// Interaction logic for ProjectEditor.xaml
/// </summary>
public partial class ProjectEditor : Page
{
private MainViewModel _mainViewModel;
public ProjectEditor(MainViewModel mainViewModel)
{
_mainViewModel = mainViewModel;
InitializeComponent();
}
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
CheckBox checkBox = sender as CheckBox;
Models.Application application = checkBox.DataContext as Models.Application;
Models.Project project = application.Parent;
project.OnPropertyChanged(new PropertyChangedEventArgs(nameof(Project.CanBeSaved)));
project.Initialize();
}
private void SaveButtonClicked(object sender, RoutedEventArgs e)
{
Project projectToSave = this.DataContext as Project;
_mainViewModel.SaveProject(projectToSave);
_mainViewModel.SwitchToMainView();
}
private void CancelButtonClicked(object sender, RoutedEventArgs e)
{
_mainViewModel.CancelLastEdit();
_mainViewModel.SwitchToMainView();
}
private void EditNameTextBoxKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
e.Handled = true;
Project project = this.DataContext as Project;
project.Name = EditNameTextBox.Text;
}
else if (e.Key == Key.Escape)
{
e.Handled = true;
Project project = this.DataContext as Project;
_mainViewModel.CancelProjectName(project);
}
}
private void EditNameTextBox_GotFocus(object sender, RoutedEventArgs e)
{
_mainViewModel.SaveProjectName(DataContext as Project);
}
private void AppBorder_MouseEnter(object sender, MouseEventArgs e)
{
Border border = sender as Border;
Models.Application app = border.DataContext as Models.Application;
app.IsHighlighted = true;
Project project = app.Parent;
project.Initialize();
}
private void AppBorder_MouseLeave(object sender, MouseEventArgs e)
{
Border border = sender as Border;
Models.Application app = border.DataContext as Models.Application;
app.IsHighlighted = false;
Project project = app.Parent;
project.Initialize();
}
private void EditNameTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
Project project = this.DataContext as Project;
project.Name = EditNameTextBox.Text;
project.OnPropertyChanged(new PropertyChangedEventArgs(nameof(Project.CanBeSaved)));
}
}
}

View File

@@ -1,122 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\Version.props" />
<PropertyGroup>
<AssemblyTitle>PowerToys.ProjectsEditor</AssemblyTitle>
<AssemblyDescription>PowerToys Projects Editor</AssemblyDescription>
<Description>PowerToys Projects Editor</Description>
<OutputType>WinExe</OutputType>
<UseWPF>true</UseWPF>
<UseWindowsForms>true</UseWindowsForms>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<AppendRuntimeIdentifierToOutputPath>false</AppendRuntimeIdentifierToOutputPath>
<GenerateSatelliteAssembliesForCore>true</GenerateSatelliteAssembliesForCore>
<OutputPath>..\..\..\..\$(Platform)\$(Configuration)</OutputPath>
<SelfContained>true</SelfContained>
<Platforms>AnyCPU;x64;ARM64</Platforms>
</PropertyGroup>
<!-- SelfContained=true requires RuntimeIdentifier to be set -->
<PropertyGroup Condition="'$(Platform)'=='x64'">
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='ARM64'">
<RuntimeIdentifier>win-arm64</RuntimeIdentifier>
</PropertyGroup>
<PropertyGroup>
<ProjectGuid>{367D7543-7DBA-4381-99F1-BF6142A996C4}</ProjectGuid>
<TargetFramework>net8.0-windows10.0.20348.0</TargetFramework>
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<Optimize>true</Optimize>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<Optimize>false</Optimize>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>images\Projects.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AssemblyName>PowerToys.ProjectsEditor</AssemblyName>
</PropertyGroup>
<ItemGroup>
<None Remove="images\DefaultIcon.ico" />
<None Remove="images\Projects.ico" />
</ItemGroup>
<ItemGroup>
<COMReference Include="IWshRuntimeLibrary">
<WrapperTool>tlbimp</WrapperTool>
<VersionMinor>0</VersionMinor>
<VersionMajor>1</VersionMajor>
<Guid>f935dc20-1cf0-11d0-adb9-00c04fd58a0b</Guid>
<Lcid>0</Lcid>
<Isolated>false</Isolated>
<EmbedInteropTypes>true</EmbedInteropTypes>
</COMReference>
<COMReference Include="Shell32">
<WrapperTool>tlbimp</WrapperTool>
<VersionMinor>0</VersionMinor>
<VersionMajor>1</VersionMajor>
<Guid>50a7e9b0-70ef-11d1-b75a-00a0c90564fe</Guid>
<Lcid>0</Lcid>
<Isolated>false</Isolated>
<EmbedInteropTypes>true</EmbedInteropTypes>
</COMReference>
</ItemGroup>
<ItemGroup>
<Content Include="images\DefaultIcon.ico">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="images\Projects.ico">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="app.manifest" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ControlzEx" />
<PackageReference Include="Microsoft.Windows.CsWinRT" />
<PackageReference Include="ModernWpfUI" />
<PackageReference Include="System.IO.Abstractions" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\GPOWrapperProjection\GPOWrapperProjection.csproj" />
<ProjectReference Include="..\..\..\common\interop\PowerToys.Interop.vcxproj" />
<ProjectReference Include="..\..\..\common\ManagedCommon\ManagedCommon.csproj" />
<ProjectReference Include="..\..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Update="Properties\Settings.Designer.cs">
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Update="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
</Project>

View File

@@ -1,477 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace ProjectsEditor.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ProjectsEditor.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to app.
/// </summary>
public static string App {
get {
return ResourceManager.GetString("App", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to App name.
/// </summary>
public static string App_name {
get {
return ResourceManager.GetString("App_name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to apps.
/// </summary>
public static string Apps {
get {
return ResourceManager.GetString("Apps", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Are you sure?.
/// </summary>
public static string Are_You_Sure {
get {
return ResourceManager.GetString("Are_You_Sure", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Are you sure you want to delete this project?.
/// </summary>
public static string Are_You_Sure_Description {
get {
return ResourceManager.GetString("Are_You_Sure_Description", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Cancel.
/// </summary>
public static string Cancel {
get {
return ResourceManager.GetString("Cancel", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Created.
/// </summary>
public static string Created {
get {
return ResourceManager.GetString("Created", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Create project.
/// </summary>
public static string CreateProject {
get {
return ResourceManager.GetString("CreateProject", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Create Shortcut.
/// </summary>
public static string CreateShortcut {
get {
return ResourceManager.GetString("CreateShortcut", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to days ago.
/// </summary>
public static string DaysAgo {
get {
return ResourceManager.GetString("DaysAgo", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Remove.
/// </summary>
public static string Delete {
get {
return ResourceManager.GetString("Delete", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Delete project dialog..
/// </summary>
public static string Delete_Project_Dialog_Announce {
get {
return ResourceManager.GetString("Delete_Project_Dialog_Announce", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Edit.
/// </summary>
public static string Edit {
get {
return ResourceManager.GetString("Edit", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to opened.
/// </summary>
public static string Edit_Project_Open_Announce {
get {
return ResourceManager.GetString("Edit_Project_Open_Announce", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Edit project.
/// </summary>
public static string EditProject {
get {
return ResourceManager.GetString("EditProject", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Error parsing projects data..
/// </summary>
public static string Error_Parsing_Message {
get {
return ResourceManager.GetString("Error_Parsing_Message", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to hours ago.
/// </summary>
public static string HoursAgo {
get {
return ResourceManager.GetString("HoursAgo", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Last launched.
/// </summary>
public static string LastLaunched {
get {
return ResourceManager.GetString("LastLaunched", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Launch.
/// </summary>
public static string Launch {
get {
return ResourceManager.GetString("Launch", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Launch args.
/// </summary>
public static string Launch_args {
get {
return ResourceManager.GetString("Launch_args", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Projects demo app.
/// </summary>
public static string MainTitle {
get {
return ResourceManager.GetString("MainTitle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to minutes ago.
/// </summary>
public static string MinutesAgo {
get {
return ResourceManager.GetString("MinutesAgo", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to months ago.
/// </summary>
public static string MonthsAgo {
get {
return ResourceManager.GetString("MonthsAgo", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Name.
/// </summary>
public static string Name {
get {
return ResourceManager.GetString("Name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to never.
/// </summary>
public static string Never {
get {
return ResourceManager.GetString("Never", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to New project.
/// </summary>
public static string New_project {
get {
return ResourceManager.GetString("New_project", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to There are no saved projects..
/// </summary>
public static string No_Projects_Message {
get {
return ResourceManager.GetString("No_Projects_Message", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No projects match the current search..
/// </summary>
public static string NoProjectsMatch {
get {
return ResourceManager.GetString("NoProjectsMatch", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The application cannot be found.
/// </summary>
public static string NotFoundTooltip {
get {
return ResourceManager.GetString("NotFoundTooltip", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to an hour ago.
/// </summary>
public static string OneHourAgo {
get {
return ResourceManager.GetString("OneHourAgo", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to a minute ago.
/// </summary>
public static string OneMinuteAgo {
get {
return ResourceManager.GetString("OneMinuteAgo", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to one month ago.
/// </summary>
public static string OneMonthAgo {
get {
return ResourceManager.GetString("OneMonthAgo", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to one second ago.
/// </summary>
public static string OneSecondAgo {
get {
return ResourceManager.GetString("OneSecondAgo", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to one year ago.
/// </summary>
public static string OneYearAgo {
get {
return ResourceManager.GetString("OneYearAgo", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Pin Project to Taskbar.
/// </summary>
public static string PinToTaskbar {
get {
return ResourceManager.GetString("PinToTaskbar", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Project name.
/// </summary>
public static string ProjectName {
get {
return ResourceManager.GetString("ProjectName", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Projects.
/// </summary>
public static string Projects {
get {
return ResourceManager.GetString("Projects", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to recently.
/// </summary>
public static string Recently {
get {
return ResourceManager.GetString("Recently", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Save project.
/// </summary>
public static string Save_project {
get {
return ResourceManager.GetString("Save_project", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Search.
/// </summary>
public static string Search {
get {
return ResourceManager.GetString("Search", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Search for projects or apps.
/// </summary>
public static string SearchExplanation {
get {
return ResourceManager.GetString("SearchExplanation", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to seconds ago.
/// </summary>
public static string SecondsAgo {
get {
return ResourceManager.GetString("SecondsAgo", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Sort by.
/// </summary>
public static string SortBy {
get {
return ResourceManager.GetString("SortBy", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Write arguments here.
/// </summary>
public static string WriteArgs {
get {
return ResourceManager.GetString("WriteArgs", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to years ago.
/// </summary>
public static string YearsAgo {
get {
return ResourceManager.GetString("YearsAgo", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to yesterday.
/// </summary>
public static string Yesterday {
get {
return ResourceManager.GetString("Yesterday", resourceCulture);
}
}
}
}

View File

@@ -1,258 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="App" xml:space="preserve">
<value>app</value>
</data>
<data name="Apps" xml:space="preserve">
<value>apps</value>
</data>
<data name="App_name" xml:space="preserve">
<value>App name</value>
</data>
<data name="Are_You_Sure" xml:space="preserve">
<value>Are you sure?</value>
</data>
<data name="Are_You_Sure_Description" xml:space="preserve">
<value>Are you sure you want to delete this project?</value>
</data>
<data name="Cancel" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="Created" xml:space="preserve">
<value>Created</value>
</data>
<data name="CreateProject" xml:space="preserve">
<value>Create project</value>
</data>
<data name="CreateShortcut" xml:space="preserve">
<value>Create Shortcut</value>
</data>
<data name="DaysAgo" xml:space="preserve">
<value>days ago</value>
</data>
<data name="Delete" xml:space="preserve">
<value>Remove</value>
</data>
<data name="Delete_Project_Dialog_Announce" xml:space="preserve">
<value>Delete project dialog.</value>
</data>
<data name="Edit" xml:space="preserve">
<value>Edit</value>
</data>
<data name="EditProject" xml:space="preserve">
<value>Edit project</value>
</data>
<data name="Edit_Project_Open_Announce" xml:space="preserve">
<value>opened</value>
</data>
<data name="Error_Parsing_Message" xml:space="preserve">
<value>Error parsing projects data.</value>
</data>
<data name="HoursAgo" xml:space="preserve">
<value>hours ago</value>
</data>
<data name="LastLaunched" xml:space="preserve">
<value>Last launched</value>
</data>
<data name="Launch" xml:space="preserve">
<value>Launch</value>
</data>
<data name="Launch_args" xml:space="preserve">
<value>Launch args</value>
</data>
<data name="MainTitle" xml:space="preserve">
<value>Projects demo app</value>
</data>
<data name="MinutesAgo" xml:space="preserve">
<value>minutes ago</value>
</data>
<data name="MonthsAgo" xml:space="preserve">
<value>months ago</value>
</data>
<data name="Name" xml:space="preserve">
<value>Name</value>
</data>
<data name="Never" xml:space="preserve">
<value>never</value>
</data>
<data name="New_project" xml:space="preserve">
<value>New project</value>
</data>
<data name="NoProjectsMatch" xml:space="preserve">
<value>No projects match the current search.</value>
</data>
<data name="NotFoundTooltip" xml:space="preserve">
<value>The application cannot be found</value>
</data>
<data name="No_Projects_Message" xml:space="preserve">
<value>There are no saved projects.</value>
</data>
<data name="OneHourAgo" xml:space="preserve">
<value>an hour ago</value>
</data>
<data name="OneMinuteAgo" xml:space="preserve">
<value>a minute ago</value>
</data>
<data name="OneMonthAgo" xml:space="preserve">
<value>one month ago</value>
</data>
<data name="OneSecondAgo" xml:space="preserve">
<value>one second ago</value>
</data>
<data name="OneYearAgo" xml:space="preserve">
<value>one year ago</value>
</data>
<data name="PinToTaskbar" xml:space="preserve">
<value>Pin Project to Taskbar</value>
</data>
<data name="ProjectName" xml:space="preserve">
<value>Project name</value>
</data>
<data name="Projects" xml:space="preserve">
<value>Projects</value>
</data>
<data name="Recently" xml:space="preserve">
<value>recently</value>
</data>
<data name="Save_project" xml:space="preserve">
<value>Save project</value>
</data>
<data name="Search" xml:space="preserve">
<value>Search</value>
</data>
<data name="SearchExplanation" xml:space="preserve">
<value>Search for projects or apps</value>
</data>
<data name="SecondsAgo" xml:space="preserve">
<value>seconds ago</value>
</data>
<data name="SortBy" xml:space="preserve">
<value>Sort by</value>
</data>
<data name="WriteArgs" xml:space="preserve">
<value>Write arguments here</value>
</data>
<data name="YearsAgo" xml:space="preserve">
<value>years ago</value>
</data>
<data name="Yesterday" xml:space="preserve">
<value>yesterday</value>
</data>
</root>

View File

@@ -1,26 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace ProjectsEditor.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.1.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}

View File

@@ -1,7 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

View File

@@ -1,55 +0,0 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ui="http://schemas.modernwpf.com/2019">
<Style
x:Key="IconOnlyButtonStyle"
BasedOn="{StaticResource DefaultButtonStyle}"
TargetType="Button">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="{DynamicResource ButtonForeground}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border
x:Name="Background"
Background="Transparent"
CornerRadius="{TemplateBinding ui:ControlHelper.CornerRadius}"
SnapsToDevicePixels="True">
<Border
x:Name="Border"
Padding="{TemplateBinding Padding}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding ui:ControlHelper.CornerRadius}">
<ContentPresenter
x:Name="ContentPresenter"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Focusable="False"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Background" Property="Background" Value="{DynamicResource ButtonBackgroundPointerOver}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushPointerOver}" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Background" Property="Background" Value="{DynamicResource ButtonBackgroundPressed}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushPressed}" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Background" Property="Background" Value="{DynamicResource ButtonBackgroundDisabled}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{DynamicResource ButtonBorderBrushDisabled}" />
<Setter TargetName="ContentPresenter" Property="TextElement.Foreground" Value="{DynamicResource ButtonForegroundDisabled}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@@ -1,25 +0,0 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=System.Runtime">
<!-- Metadata -->
<system:String x:Key="Theme.Name">Dark.Accent1</system:String>
<system:String x:Key="Theme.Origin">Origin</system:String>
<system:String x:Key="Theme.DisplayName">Accent1 (Dark)</system:String>
<system:String x:Key="Theme.BaseColorScheme">Dark</system:String>
<system:String x:Key="Theme.ColorScheme">Accent1</system:String>
<Color x:Key="Theme.PrimaryAccentColor">Black</Color>
<SolidColorBrush x:Key="PrimaryBackgroundBrush" Color="#FF242424" />
<SolidColorBrush x:Key="SecondaryBackgroundBrush" Color="#FF2b2b2b" />
<SolidColorBrush x:Key="TertiaryBackgroundBrush" Color="#FF373737" />
<SolidColorBrush x:Key="MonitorViewBackgroundBrush" Color="#FF161616" />
<SolidColorBrush x:Key="PrimaryForegroundBrush" Color="#FFFFFFFF" />
<SolidColorBrush x:Key="SecondaryForegroundBrush" Color="#FF9a9a9a" />
<SolidColorBrush x:Key="PrimaryBorderBrush" Color="#FF373737" />
<SolidColorBrush x:Key="SecondaryBorderBrush" Color="#FF494949" />
<SolidColorBrush x:Key="TertiaryBorderBrush" Color="#FF202020" />
<SolidColorBrush x:Key="BackdropBrush" Color="#40F0F0F0" />
<SolidColorBrush x:Key="TitleBarSecondaryForegroundBrush" Color="#FF9a9a9a" />
</ResourceDictionary>

View File

@@ -1,25 +0,0 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=System.Runtime">
<!-- Metadata -->
<system:String x:Key="Theme.Name">HighContrast.Accent2</system:String>
<system:String x:Key="Theme.Origin">Origin</system:String>
<system:String x:Key="Theme.DisplayName">Accent2 (HighContrast)</system:String>
<system:String x:Key="Theme.BaseColorScheme">HighContrast</system:String>
<system:String x:Key="Theme.ColorScheme">Accent2</system:String>
<Color x:Key="Theme.PrimaryAccentColor">White</Color>
<SolidColorBrush x:Key="PrimaryBackgroundBrush" Color="#FF242424" />
<SolidColorBrush x:Key="SecondaryBackgroundBrush" Color="#FF1c1c1c" />
<SolidColorBrush x:Key="TertiaryBackgroundBrush" Color="#FF202020" />
<SolidColorBrush x:Key="MonitorViewBackgroundBrush" Color="#FF161616" />
<SolidColorBrush x:Key="PrimaryForegroundBrush" Color="#FFffff00" />
<SolidColorBrush x:Key="SecondaryForegroundBrush" Color="#FF00ff00" />
<SolidColorBrush x:Key="PrimaryBorderBrush" Color="#FF373737" />
<SolidColorBrush x:Key="SecondaryBorderBrush" Color="#FF494949" />
<SolidColorBrush x:Key="TertiaryBorderBrush" Color="#FF202020" />
<SolidColorBrush x:Key="BackdropBrush" Color="#E55B5B5B" />
<SolidColorBrush x:Key="TitleBarSecondaryForegroundBrush" Color="#FF9a9a9a" />
</ResourceDictionary>

View File

@@ -1,25 +0,0 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=System.Runtime">
<!-- Metadata -->
<system:String x:Key="Theme.Name">HighContrast.Accent3</system:String>
<system:String x:Key="Theme.Origin">Origin</system:String>
<system:String x:Key="Theme.DisplayName">Accent3 (HighContrast)</system:String>
<system:String x:Key="Theme.BaseColorScheme">HighContrast</system:String>
<system:String x:Key="Theme.ColorScheme">Accent3</system:String>
<Color x:Key="Theme.PrimaryAccentColor">White</Color>
<SolidColorBrush x:Key="PrimaryBackgroundBrush" Color="#FF242424" />
<SolidColorBrush x:Key="SecondaryBackgroundBrush" Color="#FF1c1c1c" />
<SolidColorBrush x:Key="TertiaryBackgroundBrush" Color="#FF202020" />
<SolidColorBrush x:Key="MonitorViewBackgroundBrush" Color="#FF161616" />
<SolidColorBrush x:Key="PrimaryForegroundBrush" Color="#FFffff00" />
<SolidColorBrush x:Key="SecondaryForegroundBrush" Color="#FFc0c0c0" />
<SolidColorBrush x:Key="PrimaryBorderBrush" Color="#FF373737" />
<SolidColorBrush x:Key="SecondaryBorderBrush" Color="#FF494949" />
<SolidColorBrush x:Key="TertiaryBorderBrush" Color="#FF202020" />
<SolidColorBrush x:Key="BackdropBrush" Color="#E55B5B5B" />
<SolidColorBrush x:Key="TitleBarSecondaryForegroundBrush" Color="#FF9a9a9a" />
</ResourceDictionary>

View File

@@ -1,25 +0,0 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=System.Runtime">
<!-- Metadata -->
<system:String x:Key="Theme.Name">HighContrast.Accent4</system:String>
<system:String x:Key="Theme.Origin">Origin</system:String>
<system:String x:Key="Theme.DisplayName">Accent4 (HighContrast)</system:String>
<system:String x:Key="Theme.BaseColorScheme">HighContrast</system:String>
<system:String x:Key="Theme.ColorScheme">Accent4</system:String>
<Color x:Key="Theme.PrimaryAccentColor">White</Color>
<SolidColorBrush x:Key="PrimaryBackgroundBrush" Color="#FF242424" />
<SolidColorBrush x:Key="SecondaryBackgroundBrush" Color="#FF1c1c1c" />
<SolidColorBrush x:Key="TertiaryBackgroundBrush" Color="#FF202020" />
<SolidColorBrush x:Key="MonitorViewBackgroundBrush" Color="#FF161616" />
<SolidColorBrush x:Key="PrimaryForegroundBrush" Color="#FFffffff" />
<SolidColorBrush x:Key="SecondaryForegroundBrush" Color="#FF1aebff" />
<SolidColorBrush x:Key="PrimaryBorderBrush" Color="#FF373737" />
<SolidColorBrush x:Key="SecondaryBorderBrush" Color="#FF494949" />
<SolidColorBrush x:Key="TertiaryBorderBrush" Color="#FF202020" />
<SolidColorBrush x:Key="BackdropBrush" Color="#E55B5B5B" />
<SolidColorBrush x:Key="TitleBarSecondaryForegroundBrush" Color="#FF9a9a9a" />
</ResourceDictionary>

View File

@@ -1,25 +0,0 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=System.Runtime">
<!-- Metadata -->
<system:String x:Key="Theme.Name">HighContrast.Accent5</system:String>
<system:String x:Key="Theme.Origin">Origin</system:String>
<system:String x:Key="Theme.DisplayName">Accent5 (HighContrast)</system:String>
<system:String x:Key="Theme.BaseColorScheme">HighContrast</system:String>
<system:String x:Key="Theme.ColorScheme">Accent5</system:String>
<Color x:Key="Theme.PrimaryAccentColor">White</Color>
<SolidColorBrush x:Key="PrimaryBackgroundBrush" Color="#FFf9f9f9" />
<SolidColorBrush x:Key="SecondaryBackgroundBrush" Color="#FFeeeeee" />
<SolidColorBrush x:Key="TertiaryBackgroundBrush" Color="#FFF3F3F3" />
<SolidColorBrush x:Key="MonitorViewBackgroundBrush" Color="#FF161616" />
<SolidColorBrush x:Key="PrimaryForegroundBrush" Color="#FF000000" />
<SolidColorBrush x:Key="SecondaryForegroundBrush" Color="#FF37006e" />
<SolidColorBrush x:Key="PrimaryBorderBrush" Color="#FF373737" />
<SolidColorBrush x:Key="SecondaryBorderBrush" Color="#FF494949" />
<SolidColorBrush x:Key="TertiaryBorderBrush" Color="#FF202020" />
<SolidColorBrush x:Key="BackdropBrush" Color="#E5949494" />
<SolidColorBrush x:Key="TitleBarSecondaryForegroundBrush" Color="#FF949494" />
</ResourceDictionary>

View File

@@ -1,25 +0,0 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=System.Runtime">
<!-- Metadata -->
<system:String x:Key="Theme.Name">Light.Accent1</system:String>
<system:String x:Key="Theme.Origin">Origin</system:String>
<system:String x:Key="Theme.DisplayName">Accent1 (Light)</system:String>
<system:String x:Key="Theme.BaseColorScheme">Light</system:String>
<system:String x:Key="Theme.ColorScheme">Accent1</system:String>
<Color x:Key="Theme.PrimaryAccentColor">White</Color>
<SolidColorBrush x:Key="PrimaryBackgroundBrush" Color="#FFf3f3f3" />
<SolidColorBrush x:Key="SecondaryBackgroundBrush" Color="#FFfbfbfb" />
<SolidColorBrush x:Key="TertiaryBackgroundBrush" Color="#FFfefefe" />
<SolidColorBrush x:Key="MonitorViewBackgroundBrush" Color="#FFF9F9F9" />
<SolidColorBrush x:Key="PrimaryForegroundBrush" Color="#FF191919" />
<SolidColorBrush x:Key="SecondaryForegroundBrush" Color="#FF6A6A6A" />
<SolidColorBrush x:Key="PrimaryBorderBrush" Color="#FFe5e5e5" />
<SolidColorBrush x:Key="SecondaryBorderBrush" Color="#FFe5e5e5" />
<SolidColorBrush x:Key="TertiaryBorderBrush" Color="#FFe5e5e5" />
<SolidColorBrush x:Key="BackdropBrush" Color="#85F0F0F0" />
<SolidColorBrush x:Key="TitleBarSecondaryForegroundBrush" Color="#FF949494" />
</ResourceDictionary>

View File

@@ -1,18 +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.Text.Json;
namespace ProjectsEditor.Utils
{
public class DashCaseNamingPolicy : JsonNamingPolicy
{
public static DashCaseNamingPolicy Instance { get; } = new DashCaseNamingPolicy();
public override string ConvertName(string name)
{
return name.UpperCamelCaseToDashCase();
}
}
}

View File

@@ -1,347 +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.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Windows.Media.Imaging;
using ManagedCommon;
using ProjectsEditor.Models;
namespace ProjectsEditor.Utils
{
public class DrawHelper
{
private static Font font = new("Tahoma", 24);
public static BitmapImage DrawPreview(Project project, Rectangle bounds)
{
List<double> horizontalGaps = new List<double>();
List<double> verticalGaps = new List<double>();
double gapWidth = bounds.Width * 0.01;
double gapHeight = bounds.Height * 0.01;
double scale = 0.1;
int Scaled(double value)
{
return (int)(value * scale);
}
int TransformX(double posX)
{
double gapTransform = verticalGaps.Where(x => x <= posX).Count() * gapWidth;
return Scaled(posX - bounds.Left + gapTransform);
}
int TransformY(double posY)
{
double gapTransform = horizontalGaps.Where(x => x <= posY).Count() * gapHeight;
return Scaled(posY - bounds.Top + gapTransform);
}
Dictionary<string, int> repeatCounter = new Dictionary<string, int>();
var selectedApps = project.Applications.Where(x => x.IsSelected);
foreach (Application app in selectedApps)
{
if (repeatCounter.TryGetValue(app.AppPath, out int value))
{
repeatCounter[app.AppPath] = ++value;
}
else
{
repeatCounter.Add(app.AppPath, 1);
}
app.RepeatIndex = repeatCounter[app.AppPath];
}
// remove those repeatIndexes, which are single 1-es (no repetitions) by setting them to 0
foreach (Application app in selectedApps.Where(x => repeatCounter[x.AppPath] == 1))
{
app.RepeatIndex = 0;
}
foreach (Application app in project.Applications.Where(x => !x.IsSelected))
{
app.RepeatIndex = 0;
}
// now that all repeat index values are set, update the repeat index strings on UI
foreach (Application app in project.Applications)
{
app.OnPropertyChanged(new PropertyChangedEventArgs("RepeatIndexString"));
}
foreach (MonitorSetup monitor in project.Monitors)
{
// check for vertical gap
if (monitor.MonitorDpiAwareBounds.Left > bounds.Left && project.Monitors.Any(x => x.MonitorDpiAwareBounds.Right <= monitor.MonitorDpiAwareBounds.Left))
{
verticalGaps.Add(monitor.MonitorDpiAwareBounds.Left);
}
// check for horizontal gap
if (monitor.MonitorDpiAwareBounds.Top > bounds.Top && project.Monitors.Any(x => x.MonitorDpiAwareBounds.Bottom <= monitor.MonitorDpiAwareBounds.Top))
{
horizontalGaps.Add(monitor.MonitorDpiAwareBounds.Top);
}
}
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.3;
using (Graphics g = Graphics.FromImage(previewBitmap))
{
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
Brush brush = new SolidBrush(Common.ThemeManager.GetCurrentTheme() == Common.Theme.Dark ? Color.FromArgb(10, 255, 255, 255) : Color.FromArgb(10, 0, 0, 0));
// draw the monitors
foreach (MonitorSetup monitor in project.Monitors)
{
Brush monitorBrush = new SolidBrush(Common.ThemeManager.GetCurrentTheme() == Common.Theme.Dark ? Color.FromArgb(32, 7, 91, 155) : Color.FromArgb(32, 7, 91, 155));
g.FillRectangle(monitorBrush, new Rectangle(TransformX(monitor.MonitorDpiAwareBounds.Left), TransformY(monitor.MonitorDpiAwareBounds.Top), Scaled(monitor.MonitorDpiAwareBounds.Width), Scaled(monitor.MonitorDpiAwareBounds.Height)));
}
var appsToDraw = project.Applications.Where(x => x.IsSelected && !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))
{
Rectangle rect = new Rectangle(TransformX(app.ScaledPosition.X), TransformY(app.ScaledPosition.Y), Scaled(app.ScaledPosition.Width), Scaled(app.ScaledPosition.Height));
DrawWindow(g, brush, rect, app, desiredIconSize);
}
foreach (Application app in appsToDraw.Where(x => x.IsHighlighted))
{
Rectangle rect = new Rectangle(TransformX(app.ScaledPosition.X), TransformY(app.ScaledPosition.Y), Scaled(app.ScaledPosition.Width), Scaled(app.ScaledPosition.Height));
DrawWindow(g, brush, rect, app, desiredIconSize);
}
// draw the minimized windows
Rectangle rectMinimized = new Rectangle(0, Scaled((bounds.Height * 1.02) + (horizontalGaps.Count * gapHeight)), Scaled(bounds.Width + (verticalGaps.Count * gapWidth)), Scaled(bounds.Height * 0.18));
DrawWindow(g, brush, rectMinimized, project.Applications.Where(x => x.IsSelected && x.Minimized));
}
using (var memory = new MemoryStream())
{
previewBitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
}
public static void DrawWindow(Graphics graphics, Brush brush, Rectangle bounds, Application app, double desiredIconSize)
{
if (graphics == null)
{
return;
}
if (brush == null)
{
return;
}
using (GraphicsPath path = RoundedRect(bounds))
{
if (app.IsHighlighted)
{
graphics.DrawPath(new Pen(Common.ThemeManager.GetCurrentTheme() == Common.Theme.Dark ? Color.White : Color.DarkGray, graphics.VisibleClipBounds.Height / 50), path);
}
else
{
graphics.DrawPath(new Pen(Common.ThemeManager.GetCurrentTheme() == Common.Theme.Dark ? Color.FromArgb(128, 82, 82, 82) : Color.FromArgb(128, 160, 160, 160), graphics.VisibleClipBounds.Height / 200), path);
}
graphics.FillPath(brush, path);
}
double iconSize = Math.Min(Math.Min(bounds.Width - 4, bounds.Height - 4), desiredIconSize);
Rectangle iconBounds = new Rectangle((int)(bounds.Left + (bounds.Width / 2) - (iconSize / 2)), (int)(bounds.Top + (bounds.Height / 2) - (iconSize / 2)), (int)iconSize, (int)iconSize);
try
{
graphics.DrawIcon(app.Icon, iconBounds);
if (app.RepeatIndex > 0)
{
string indexString = app.RepeatIndex.ToString(CultureInfo.InvariantCulture);
int indexSize = (int)(iconBounds.Width * 0.5);
Rectangle indexBounds = new Rectangle(iconBounds.Right - indexSize, iconBounds.Bottom - indexSize, indexSize, indexSize);
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.Restore(state);
}
}
catch (Exception)
{
// sometimes drawing an icon throws an exception despite that the icon seems to be ok
}
}
public static void DrawWindow(Graphics graphics, Brush brush, Rectangle bounds, IEnumerable<Application> apps)
{
int appsCount = apps.Count();
if (appsCount == 0)
{
return;
}
if (graphics == null)
{
return;
}
if (brush == null)
{
return;
}
using (GraphicsPath path = RoundedRect(bounds))
{
if (apps.Where(x => x.IsHighlighted).Any())
{
graphics.DrawPath(new Pen(Common.ThemeManager.GetCurrentTheme() == Common.Theme.Dark ? Color.White : Color.DarkGray, graphics.VisibleClipBounds.Height / 50), path);
}
else
{
graphics.DrawPath(new Pen(Common.ThemeManager.GetCurrentTheme() == Common.Theme.Dark ? Color.FromArgb(128, 82, 82, 82) : Color.FromArgb(128, 160, 160, 160), graphics.VisibleClipBounds.Height / 200), path);
}
graphics.FillPath(brush, path);
}
double iconSize = Math.Min(bounds.Width, bounds.Height) * 0.5;
for (int iconCounter = 0; iconCounter < appsCount; iconCounter++)
{
Application app = apps.ElementAt(iconCounter);
Rectangle iconBounds = new Rectangle((int)(bounds.Left + (bounds.Width / 2) - (iconSize * ((appsCount / 2) - iconCounter))), (int)(bounds.Top + (bounds.Height / 2) - (iconSize / 2)), (int)iconSize, (int)iconSize);
try
{
graphics.DrawIcon(app.Icon, iconBounds);
if (app.RepeatIndex > 0)
{
string indexString = app.RepeatIndex.ToString(CultureInfo.InvariantCulture);
int indexSize = (int)(iconBounds.Width * 0.5);
Rectangle indexBounds = new Rectangle(iconBounds.Right - indexSize, iconBounds.Bottom - indexSize, indexSize, indexSize);
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.Restore(state);
}
}
catch (Exception)
{
// sometimes drawing an icon throws an exception despite that the icon seems to be ok
}
}
}
public static BitmapImage DrawPreviewIcons(Project project)
{
var selectedApps = project.Applications.Where(x => x.IsSelected);
int appsCount = selectedApps.Count();
if (appsCount == 0)
{
return null;
}
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 (var app in selectedApps)
{
try
{
graphics.DrawIcon(app.Icon, new Rectangle(32 * appIndex, 0, 24, 24));
}
catch (Exception e)
{
Logger.LogError($"Exception while drawing the icon for app {app.AppName}. Exception message: {e.Message}");
}
appIndex++;
}
}
using (var memory = new MemoryStream())
{
previewBitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
bitmapImage.Freeze();
return bitmapImage;
}
}
private static GraphicsPath RoundedRect(Rectangle bounds)
{
int minorSize = Math.Min(bounds.Width, bounds.Height);
int radius = (int)(minorSize / 8);
int diameter = radius * 2;
Size size = new Size(diameter, diameter);
Rectangle arc = new Rectangle(bounds.Location, size);
GraphicsPath path = new GraphicsPath();
if (radius == 0)
{
path.AddRectangle(bounds);
return path;
}
// top left arc
path.AddArc(arc, 180, 90);
// top right arc
arc.X = bounds.Right - diameter;
path.AddArc(arc, 270, 90);
// bottom right arc
arc.Y = bounds.Bottom - diameter;
path.AddArc(arc, 0, 90);
// bottom left arc
arc.X = bounds.Left;
path.AddArc(arc, 90, 90);
path.CloseFigure();
return path;
}
}
}

View File

@@ -1,28 +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.IO;
namespace ProjectsEditor.Utils
{
public class FolderUtils
{
public static string Desktop()
{
return Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
}
public static string Temp()
{
return Path.GetTempPath();
}
// Note: the same path should be used in SnapshotTool and Launcher
public static string DataFolder()
{
return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\Microsoft\\PowerToys\\Projects";
}
}
}

View File

@@ -1,54 +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.IO;
using System.IO.Abstractions;
using System.Threading.Tasks;
namespace ProjectsEditor.Utils
{
public class IOUtils
{
private readonly IFileSystem _fileSystem = new FileSystem();
public IOUtils()
{
}
public void WriteFile(string fileName, string data)
{
_fileSystem.File.WriteAllText(fileName, data);
}
public string ReadFile(string fileName)
{
if (_fileSystem.File.Exists(fileName))
{
var attempts = 0;
while (attempts < 10)
{
try
{
using (Stream inputStream = _fileSystem.File.Open(fileName, FileMode.Open))
using (StreamReader reader = new StreamReader(inputStream))
{
string data = reader.ReadToEnd();
inputStream.Close();
return data;
}
}
catch (Exception)
{
Task.Delay(10).Wait();
}
attempts++;
}
}
return string.Empty;
}
}
}

View File

@@ -1,41 +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.Runtime.InteropServices;
namespace ProjectsEditor.Utils
{
internal sealed class NativeMethods
{
public const int SW_RESTORE = 9;
public const int SW_NORMAL = 1;
public const int SW_MINIMIZE = 6;
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll")]
public static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
public static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr processId);
[DllImport("kernel32.dll")]
public static extern uint GetCurrentThreadId();
[DllImport("user32.dll")]
public static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);
}
}

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