mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-01-26 14:07:24 +01:00
Compare commits
30 Commits
yuleng/cmd
...
yuleng/cmd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f00eed43e8 | ||
|
|
c4fdb71fa0 | ||
|
|
fe53a9c89a | ||
|
|
a708a3afaa | ||
|
|
b7f99e88ef | ||
|
|
6f43aac26a | ||
|
|
d7e826d2ac | ||
|
|
9e8754a592 | ||
|
|
49687251d3 | ||
|
|
f72ccd12fd | ||
|
|
4b44858a48 | ||
|
|
53ae118e72 | ||
|
|
31df702704 | ||
|
|
ea2542b235 | ||
|
|
c6776d0d45 | ||
|
|
d48286a3eb | ||
|
|
7a5b25cd3e | ||
|
|
1eccbc3021 | ||
|
|
ce620e427f | ||
|
|
3d28d8e501 | ||
|
|
32492772b8 | ||
|
|
6896f59d48 | ||
|
|
7ce347149f | ||
|
|
626d43f631 | ||
|
|
83aff2687d | ||
|
|
f8837c4ed0 | ||
|
|
9589d3bd74 | ||
|
|
f3b10bfa8e | ||
|
|
3e7c7d77df | ||
|
|
0badb19936 |
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -11,7 +11,7 @@ body:
|
||||
- type: input
|
||||
attributes:
|
||||
label: Microsoft PowerToys version
|
||||
placeholder: 0.70.0
|
||||
placeholder: X.XX.X
|
||||
description: Hover over system tray icon or look at Settings
|
||||
validations:
|
||||
required: true
|
||||
|
||||
1
.github/actions/spell-check/allow/names.txt
vendored
1
.github/actions/spell-check/allow/names.txt
vendored
@@ -110,6 +110,7 @@ Lambson
|
||||
Laute
|
||||
laviusmotileng
|
||||
Leilei
|
||||
Loewen
|
||||
Luecking
|
||||
Mahalingam
|
||||
Markovic
|
||||
|
||||
14
.github/actions/spell-check/expect.txt
vendored
14
.github/actions/spell-check/expect.txt
vendored
@@ -15,6 +15,7 @@ ACTIVATEAPP
|
||||
activationaction
|
||||
ACVS
|
||||
adaptivecards
|
||||
ADate
|
||||
ADDSTRING
|
||||
ADDUNDORECORD
|
||||
ADifferent
|
||||
@@ -199,8 +200,7 @@ CLIPCHILDREN
|
||||
CLIPSIBLINGS
|
||||
closesocket
|
||||
CLSCTX
|
||||
CLSIDs
|
||||
Clsids
|
||||
clsids
|
||||
Clusion
|
||||
cmder
|
||||
CMDNOTFOUNDMODULEINTERFACE
|
||||
@@ -417,6 +417,7 @@ DWORDLONG
|
||||
dworigin
|
||||
dwrite
|
||||
dxgi
|
||||
eab
|
||||
easeofaccess
|
||||
ecount
|
||||
Edid
|
||||
@@ -466,6 +467,7 @@ EXAND
|
||||
EXCLUDEFROMCAPTURE
|
||||
executionpolicy
|
||||
exename
|
||||
exf
|
||||
EXITSIZEMOVE
|
||||
exlist
|
||||
EXPCMDFLAGS
|
||||
@@ -898,6 +900,7 @@ MARKDOWNPREVIEWHANDLERCPP
|
||||
MAXIMIZEBOX
|
||||
MAXSHORTCUTSIZE
|
||||
maxversiontested
|
||||
mber
|
||||
MBM
|
||||
MBR
|
||||
MDICHILD
|
||||
@@ -1779,6 +1782,8 @@ USRDLL
|
||||
UType
|
||||
uuidv
|
||||
uwp
|
||||
ums
|
||||
uxt
|
||||
uxtheme
|
||||
vabdq
|
||||
validmodulename
|
||||
@@ -1859,10 +1864,10 @@ webbrowsers
|
||||
webpage
|
||||
websites
|
||||
wekyb
|
||||
wft
|
||||
wgpocpl
|
||||
WHEREID
|
||||
Wholegrain
|
||||
WIC
|
||||
wic
|
||||
wifi
|
||||
wil
|
||||
@@ -1919,6 +1924,7 @@ WNDCLASSEXW
|
||||
WNDCLASSW
|
||||
WNDPROC
|
||||
wnode
|
||||
wom
|
||||
WORKSPACESEDITOR
|
||||
WORKSPACESLAUNCHER
|
||||
WORKSPACESSNAPSHOTTOOL
|
||||
@@ -1990,4 +1996,4 @@ zoomit
|
||||
ZOOMITX
|
||||
ZXk
|
||||
ZXNs
|
||||
zzz
|
||||
zzz
|
||||
|
||||
2
.github/workflows/msstore-submissions.yml
vendored
2
.github/workflows/msstore-submissions.yml
vendored
@@ -40,7 +40,7 @@ jobs:
|
||||
echo powerToysInstallerArm64Url=$(jq -n "$powerToysSetup" | jq -r '[.[]|select(.name | contains("arm64"))][0].browser_download_url') >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Setup .NET 9.0
|
||||
uses: actions/setup-dotnet@v3
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '9.0.x'
|
||||
|
||||
|
||||
22
COMMUNITY.md
22
COMMUNITY.md
@@ -16,7 +16,7 @@ Christian contributed New+ utility
|
||||
CleanCodeDeveloper helped do massive amounts of code stability and image resizer work.
|
||||
|
||||
### [@plante-msft](https://github.com/plante-msft) - Connor Plante
|
||||
Connor was the creator of Workspaces and helped create PowerToys Run v2
|
||||
Connor was the creator of Workspaces and helped create Command Palette (PowerToys Run v2)
|
||||
|
||||
### [@damienleroy](https://github.com/damienleroy) - [Damien Leroy](https://www.linkedin.com/in/Damien-Leroy-b2734416a/)
|
||||
Damien has helped out by developing and contributing the Quick Accent utility.
|
||||
@@ -46,7 +46,7 @@ Jeff added in multiple new features into Keyboard manager, such as key chord sup
|
||||
Joe has helped triaging, discussing, issues as well as fixing bugs and building features for Text Extractor.
|
||||
|
||||
### [@joadoumie](https://github.com/joadoumie) - Jordi Adoumie
|
||||
Jordi helped innovate amazing new features into Advanced Paste and helped create PowerToys Run v2
|
||||
Jordi helped innovate amazing new features into Advanced Paste and helped create Command Palette (PowerToys Run v2)
|
||||
|
||||
### [@jsoref](https://github.com/jsoref) - [Josh Soref](https://check-spelling.dev/)
|
||||
Helping keep our spelling correct :)
|
||||
@@ -57,6 +57,9 @@ Color Picker is from Martin.
|
||||
### [@mikeclayton](https://github.com/mikeclayton) - [Michael Clayton](https://michael-clayton.com)
|
||||
Michael contributed the [initial version](https://github.com/microsoft/PowerToys/issues/23216) of the Mouse Jump tool and [a number of updates](https://github.com/microsoft/PowerToys/pulls?q=is%3Apr+author%3Amikeclayton) based on his FancyMouse utility.
|
||||
|
||||
### [@pedrolamas](https://github.com/pedrolamas/) - Pedro Lamas
|
||||
Pedro helped create the thumbnail and File Explorer previewers for 3D files like STL and GCode. If you like 3D printing, these are very helpful.
|
||||
|
||||
### [@PesBandi](https://github.com/PesBandi/) - PesBandi
|
||||
PesBandi has helped do massive amounts of Quick Accent and bug fixes.
|
||||
|
||||
@@ -184,15 +187,10 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter
|
||||
- [@crutkas](https://github.com/crutkas/) - Clint Rutkas - Lead
|
||||
- [@cinnamon-msft](https://github.com/cinnamon-msft) - Kayla Cinnamon - Lead
|
||||
- [@nguyen-dows](https://github.com/nguyen-dows) - Christopher Nguyen - Product Manager
|
||||
- [@jaimecbernardo](https://github.com/jaimecbernardo) - Jaime Bernardo - Dev lead
|
||||
- [@craigloewen-msft](https://github.com/craigloewen-msft) - Craig Loewen - Product Manager
|
||||
- [@dhowett](https://github.com/dhowett) - Dustin Howett - Dev lead
|
||||
- [@yeelam-gordon](https://github.com/yeelam-gordon) - Gordon Lam - Dev lead
|
||||
- [@jamrobot](https://github.com/jamrobot) - Jerry Xu - Dev lead
|
||||
- [@drawbyperpetual](https://github.com/drawbyperpetual) - Anirudha Shankar - Dev
|
||||
- [@mantaionut](https://github.com/mantaionut) - Ionut Manta - Dev
|
||||
- [@donlaci](https://github.com/donlaci) - Laszlo Nemeth - Dev
|
||||
- [@SeraphimaZykova](https://github.com/SeraphimaZykova) - Seraphima Zykova - Dev
|
||||
- [@stefansjfw](https://github.com/stefansjfw) - Stefan Markovic - Dev
|
||||
- [@lei9444](https://github.com/lei9444) - Leilei Zhang - Dev
|
||||
- [@shuaiyuanxx](https://github.com/shuaiyuanxx) - Shawn Yuan - Dev
|
||||
- [@moooyo](https://github.com/moooyo) - Yu Leng - Dev
|
||||
@@ -206,7 +204,7 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter
|
||||
- [@wang563681252](https://github.com/wang563681252) - Zhaopeng Wang - Dev
|
||||
- [@vanzue](https://github.com/vanzue) - Kai Tao - Dev
|
||||
|
||||
# Former PowerToys core team members
|
||||
## Former PowerToys core team members
|
||||
|
||||
- [@indierawk2k2](https://github.com/indierawk2k2) - Mike Harsh - Product Manager
|
||||
- [@ethanfangg](https://github.com/ethanfangg) - Ethan Fang - Product Manager
|
||||
@@ -219,3 +217,9 @@ ZoomIt source code was originally implemented by [Sysinternals](https://sysinter
|
||||
- [@taras-janea](https://github.com/taras-janea) - Taras Sich - Dev
|
||||
- [@yuyoyuppe](https://github.com/yuyoyuppe) - Andrey Nekrasov - Dev
|
||||
- [@gokcekantarci](https://github.com/gokcekantarci) - Gokce Kantarci - Dev
|
||||
- [@drawbyperpetual](https://github.com/drawbyperpetual) - Anirudha Shankar - Dev
|
||||
- [@mantaionut](https://github.com/mantaionut) - Ionut Manta - Dev
|
||||
- [@donlaci](https://github.com/donlaci) - Laszlo Nemeth - Dev
|
||||
- [@SeraphimaZykova](https://github.com/SeraphimaZykova) - Seraphima Zykova - Dev
|
||||
- [@stefansjfw](https://github.com/stefansjfw) - Stefan Markovic - Dev
|
||||
- [@jaimecbernardo](https://github.com/jaimecbernardo) - Jaime Bernardo - Dev lead
|
||||
|
||||
@@ -31,22 +31,22 @@
|
||||
<!-- Including MessagePack to force version, since it's used by StreamJsonRpc but contains vulnerabilities. After StreamJsonRpc updates the version of MessagePack, we can upgrade StreamJsonRpc instead. -->
|
||||
<PackageVersion Include="MessagePack" Version="2.5.187" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Data.Sqlite" Version="9.0.4" />
|
||||
<!-- Including Microsoft.Bcl.AsyncInterfaces to force version, since it's used by Microsoft.SemanticKernel. -->
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="3.1.16" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.SemanticKernel" Version="1.15.0" />
|
||||
<PackageVersion Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
|
||||
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.2903.40" />
|
||||
<!-- Package Microsoft.Win32.SystemEvents added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Drawing.Common but the 8.0.1 version wasn't published to nuget. -->
|
||||
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.WindowsPackageManager.ComInterop" Version="1.10.120-preview" />
|
||||
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="9.0.3" />
|
||||
<PackageVersion Include="Microsoft.Windows.Compatibility" Version="9.0.4" />
|
||||
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.2.46-beta" />
|
||||
<!-- CsWinRT version needs to be set to have a WinRT.Runtime.dll at the same version contained inside the NET SDK we're currently building on CI. -->
|
||||
<!--
|
||||
@@ -72,26 +72,26 @@
|
||||
<PackageVersion Include="StreamJsonRpc" Version="2.19.27" />
|
||||
<PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
|
||||
<!-- Package System.CodeDom added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Management but the 8.0.1 version wasn't published to nuget. -->
|
||||
<PackageVersion Include="System.CodeDom" Version="9.0.3" />
|
||||
<PackageVersion Include="System.CodeDom" Version="9.0.4" />
|
||||
<PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
||||
<PackageVersion Include="System.ComponentModel.Composition" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Data.OleDb" Version="9.0.3" />
|
||||
<PackageVersion Include="System.ComponentModel.Composition" Version="9.0.4" />
|
||||
<PackageVersion Include="System.Configuration.ConfigurationManager" Version="9.0.4" />
|
||||
<PackageVersion Include="System.Data.OleDb" Version="9.0.4" />
|
||||
<!-- Package System.Data.SqlClient added to force it as a dependency of Microsoft.Windows.Compatibility to the latest version available at this time. -->
|
||||
<PackageVersion Include="System.Data.SqlClient" Version="4.8.6" />
|
||||
<!-- Package System.Diagnostics.EventLog added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.1. This is a dependency of System.Data.OleDb but the 8.0.1 version wasn't published to nuget. -->
|
||||
<PackageVersion Include="System.Diagnostics.EventLog" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Diagnostics.EventLog" Version="9.0.4" />
|
||||
<!-- Package System.Diagnostics.PerformanceCounter added as a hack for being able to exclude the runtime assets so they don't conflict with 8.0.11. -->
|
||||
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Diagnostics.PerformanceCounter" Version="9.0.4" />
|
||||
<PackageVersion Include="System.Drawing.Common" Version="9.0.4" />
|
||||
<PackageVersion Include="System.IO.Abstractions" Version="21.0.29" />
|
||||
<PackageVersion Include="System.IO.Abstractions.TestingHelpers" Version="21.0.29" />
|
||||
<PackageVersion Include="System.Management" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Management" Version="9.0.4" />
|
||||
<PackageVersion Include="System.Reactive" Version="6.0.1" />
|
||||
<PackageVersion Include="System.Runtime.Caching" Version="9.0.3" />
|
||||
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Text.Json" Version="9.0.3" />
|
||||
<PackageVersion Include="System.Runtime.Caching" Version="9.0.4" />
|
||||
<PackageVersion Include="System.ServiceProcess.ServiceController" Version="9.0.4" />
|
||||
<PackageVersion Include="System.Text.Encoding.CodePages" Version="9.0.4" />
|
||||
<PackageVersion Include="System.Text.Json" Version="9.0.4" />
|
||||
<PackageVersion Include="UnicodeInformation" Version="2.6.0" />
|
||||
<PackageVersion Include="UnitsNet" Version="5.56.0" />
|
||||
<PackageVersion Include="UTF.Unknown" Version="2.5.1" />
|
||||
|
||||
42
NOTICE.md
42
NOTICE.md
@@ -1420,22 +1420,22 @@ SOFTWARE.
|
||||
- Mages 3.0.0
|
||||
- Markdig.Signed 0.34.0
|
||||
- MessagePack 2.5.187
|
||||
- Microsoft.Bcl.AsyncInterfaces 9.0.3
|
||||
- Microsoft.Bcl.AsyncInterfaces 9.0.4
|
||||
- Microsoft.CodeAnalysis.NetAnalyzers 9.0.0
|
||||
- Microsoft.Data.Sqlite 9.0.3
|
||||
- Microsoft.Data.Sqlite 9.0.4
|
||||
- Microsoft.Diagnostics.Tracing.TraceEvent 3.1.16
|
||||
- Microsoft.DotNet.ILCompiler (A)
|
||||
- Microsoft.Extensions.DependencyInjection 9.0.3
|
||||
- Microsoft.Extensions.Hosting 9.0.3
|
||||
- Microsoft.Extensions.Hosting.WindowsServices 9.0.3
|
||||
- Microsoft.Extensions.Logging 9.0.3
|
||||
- Microsoft.Extensions.Logging.Abstractions 9.0.3
|
||||
- Microsoft.Extensions.DependencyInjection 9.0.4
|
||||
- Microsoft.Extensions.Hosting 9.0.4
|
||||
- Microsoft.Extensions.Hosting.WindowsServices 9.0.4
|
||||
- Microsoft.Extensions.Logging 9.0.4
|
||||
- Microsoft.Extensions.Logging.Abstractions 9.0.4
|
||||
- Microsoft.NET.ILLink.Tasks (A)
|
||||
- Microsoft.SemanticKernel 1.15.0
|
||||
- Microsoft.Toolkit.Uwp.Notifications 7.1.2
|
||||
- Microsoft.Web.WebView2 1.0.2903.40
|
||||
- Microsoft.Win32.SystemEvents 9.0.3
|
||||
- Microsoft.Windows.Compatibility 9.0.3
|
||||
- Microsoft.Win32.SystemEvents 9.0.4
|
||||
- Microsoft.Windows.Compatibility 9.0.4
|
||||
- Microsoft.Windows.CsWin32 0.2.46-beta
|
||||
- Microsoft.Windows.CsWinRT 2.2.0
|
||||
- Microsoft.Windows.SDK.BuildTools 10.0.22621.2428
|
||||
@@ -1454,23 +1454,23 @@ SOFTWARE.
|
||||
- SharpCompress 0.37.2
|
||||
- StreamJsonRpc 2.19.27
|
||||
- StyleCop.Analyzers 1.2.0-beta.556
|
||||
- System.CodeDom 9.0.3
|
||||
- System.CodeDom 9.0.4
|
||||
- System.CommandLine 2.0.0-beta4.22272.1
|
||||
- System.ComponentModel.Composition 9.0.3
|
||||
- System.Configuration.ConfigurationManager 9.0.3
|
||||
- System.Data.OleDb 9.0.3
|
||||
- System.ComponentModel.Composition 9.0.4
|
||||
- System.Configuration.ConfigurationManager 9.0.4
|
||||
- System.Data.OleDb 9.0.4
|
||||
- System.Data.SqlClient 4.8.6
|
||||
- System.Diagnostics.EventLog 9.0.3
|
||||
- System.Diagnostics.PerformanceCounter 9.0.3
|
||||
- System.Drawing.Common 9.0.3
|
||||
- System.Diagnostics.EventLog 9.0.4
|
||||
- System.Diagnostics.PerformanceCounter 9.0.4
|
||||
- System.Drawing.Common 9.0.4
|
||||
- System.IO.Abstractions 21.0.29
|
||||
- System.IO.Abstractions.TestingHelpers 21.0.29
|
||||
- System.Management 9.0.3
|
||||
- System.Management 9.0.4
|
||||
- System.Reactive 6.0.1
|
||||
- System.Runtime.Caching 9.0.3
|
||||
- System.ServiceProcess.ServiceController 9.0.3
|
||||
- System.Text.Encoding.CodePages 9.0.3
|
||||
- System.Text.Json 9.0.3
|
||||
- System.Runtime.Caching 9.0.4
|
||||
- System.ServiceProcess.ServiceController 9.0.4
|
||||
- System.Text.Encoding.CodePages 9.0.4
|
||||
- System.Text.Json 9.0.4
|
||||
- UnicodeInformation 2.6.0
|
||||
- UnitsNet 5.56.0
|
||||
- UTF.Unknown 2.5.1
|
||||
|
||||
@@ -101,7 +101,7 @@ In this release, we focused on new features, stability, and automation.
|
||||
|
||||

|
||||
|
||||
- New module: Command Palette ("CmdPal") - Created as the evolution of PowerToys Run with extensibility at the forefront, Command Palette is a quick launcher with a richer display and additional capabilities without sacrificing performance, allowing you to start anything with the shortcut **Win+Alt+Space**! Thanks [@zadjii-msft](https://github.com/zadjii-msft), [@niels9001](https://github.com/niels9001), [@joadoumie](https://github.com/joadoumie), [@plante-msft](https://github.com/plante-msft), [@ethanfangg](https://github.com/ethanfangg) and [@krschau](https://github.com/krschau)!
|
||||
- New module: Command Palette ("CmdPal") - Created as the evolution of PowerToys Run with extensibility at the forefront, Command Palette is a quick launcher with a richer display and additional capabilities without sacrificing performance, allowing you to start anything with the shortcut **Win+Alt+Space**! Thanks [@zadjii-msft](https://github.com/zadjii-msft), [@niels9001](https://github.com/niels9001), [@michael-hawker](https://github.com/michael-hawker), [@joadoumie](https://github.com/joadoumie), [@plante-msft](https://github.com/plante-msft), [@ethanfangg](https://github.com/ethanfangg) and [@krschau](https://github.com/krschau)!
|
||||
- Enhanced the Color Picker by switching from WPF UI to .NET WPF, resulting in improved themes and visual consistency across different modes. Thanks [@mantaionut](https://github.com/mantaionut)! Thanks [@Jay-o-Way](https://github.com/Jay-o-Way) and [@niels9001](https://github.com/niels9001) for helping with the review!
|
||||
- Added the ability to delete files directly from Peek, enhancing file management efficiency. Thanks [@daverayment](https://github.com/daverayment) and thanks [@htcfreek](https://github.com/htcfreek) for the review!
|
||||
- Added support for variables in template filenames, enabling dynamic elements like date components and environment variables for enhanced customization in New+. Thanks [@cgaarden](https://github.com/cgaarden)!
|
||||
@@ -112,7 +112,7 @@ In this release, we focused on new features, stability, and automation.
|
||||
|
||||
### Command Palette
|
||||
|
||||
- Introduced the Windows Command Palette ("CmdPal"), the next iteration of PowerToys Run, designed with extensibility at its core. CmdPal includes features such as searching for installed apps, shell commands, files and WinGet package installation. This module aims to provide a more powerful and flexible launcher experience. Thanks [@zadjii-msft](https://github.com/zadjii-msft), [@niels9001](https://github.com/niels9001), [@joadoumie](https://github.com/joadoumie), [@plante-msft](https://github.com/plante-msft), and the whole team!
|
||||
- Introduced the Windows Command Palette ("CmdPal"), the next iteration of PowerToys Run, designed with extensibility at its core. CmdPal includes features such as searching for installed apps, shell commands, files and WinGet package installation. This module aims to provide a more powerful and flexible launcher experience. Thanks [@zadjii-msft](https://github.com/zadjii-msft), [@niels9001](https://github.com/niels9001), [@michael-hawker](https://github.com/michael-hawker), [@joadoumie](https://github.com/joadoumie), [@plante-msft](https://github.com/plante-msft), and the whole team!
|
||||
|
||||
### FancyZones
|
||||
|
||||
|
||||
@@ -88,4 +88,4 @@ namespace UITests_KeyboardManager
|
||||
## Extra tools and information
|
||||
|
||||
**Accessibility Tools**:
|
||||
While working on tests, you may need a tool that helps you to view the element's accessibility data, e.g. for finding the button to click. For this purpose, you could use [AccessibilityInsights](https://accessibilityinsights.io/docs/windows/overview)
|
||||
While working on tests, you may need a tool that helps you to view the element's accessibility data, e.g. for finding the button to click. For this purpose, you could use [AccessibilityInsights](https://accessibilityinsights.io/docs/windows/overview).
|
||||
|
||||
@@ -16,49 +16,70 @@ The 'Time and Date' plugin shows the date and time in different formats. For the
|
||||
### Available formats
|
||||
|
||||
**Remarks**
|
||||
- The following formats requires a prefix in the query:
|
||||
- The following formats requires a prefix in the query when using them as date input:
|
||||
- Unix Timestamp: `u`
|
||||
- Unix Timestamp in milliseconds: `ums`
|
||||
- Windows file time: `ft`
|
||||
- OLE Automation date: `oa`
|
||||
- Excel 1900 date value: `exc`
|
||||
- Excel 1904 date value: `exf`
|
||||
- On invalid number inputs we show a warning that tells the user which prefixes are allowed/required.
|
||||
|
||||
**List of available formats**
|
||||
|
||||
The following formats are currently available:
|
||||
|
||||
| Format | Example (Based on default settings) | As result | As input |
|
||||
| Format | Example (Based on default settings) | As result | As input | Result as custom format only
|
||||
|--------------|-----------|------------|------------|
|
||||
| Time | 5:10 PM | x | x |
|
||||
| Date | 3/5/2022 | x | x |
|
||||
| Now | 3/5/2022 5:10 PM | x | x |
|
||||
| Time UTC | 4:10 PM | x | x |
|
||||
| Now UTC | 3/5/2022 4:10 PM | x | x |
|
||||
| Unix Timestamp | 1646496622 | x | x |
|
||||
| Unix Timestamp in milliseconds | 1646496622500 | x | x |
|
||||
| Hour | 10 | x | |
|
||||
| Minute | 30 | x | |
|
||||
| Second | 45 | x | |
|
||||
| Millisecond | 678 | x | |
|
||||
| Day (Week day) | Saturday | x | |
|
||||
| Day of the week | 6 | x | |
|
||||
| Day of the month | 5 | x | |
|
||||
| Day of the year | 64 | x | |
|
||||
| Week of the month | 1 | x | |
|
||||
| Week of the year (Calendar week, Week number) | 10 | x | |
|
||||
| Month | March | x | |
|
||||
| Month of the year | 3 | x | |
|
||||
| Month and day | March 7 | x | x |
|
||||
| Year | 2022 | x | |
|
||||
| Era | AD | x | |
|
||||
| Era abbreviation | A | x | |
|
||||
| Month and year | March 2022 | x | x |
|
||||
| Windows file time (Int64 number) | 637820976123938199 | x | x |
|
||||
| Universal time format: YYYY-MM-DD hh:mm:ss| 2022-03-05 16:20:12Z | x | x |
|
||||
| ISO 8601 | 2022-03-05T17:23:04 | x | x |
|
||||
| ISO 8601 UTC | 2022-03-05T16:23:04 | x | x |
|
||||
| ISO 8601 with time zone | 2022-03-05T17:23:04+01:00 | x | x |
|
||||
| ISO 8601 UTC with time zone | 2022-03-05T16:23:04Z | x | x |
|
||||
| RFC1123 | Sat, 05 Mar 2022 16:23:04 GMT | x | x |
|
||||
| Time | 5:10 PM | x | x | |
|
||||
| Date | 3/5/2022 | x | x | |
|
||||
| Now | 3/5/2022 5:10 PM | x | x | |
|
||||
| Time UTC | 4:10 PM | x | x | |
|
||||
| Now UTC | 3/5/2022 4:10 PM | x | x | |
|
||||
| Unix Timestamp | 1646496622 | x | x | |
|
||||
| Unix Timestamp in milliseconds | 1646496622500 | x | x | |
|
||||
| Hour | 10 | x | | |
|
||||
| Minute | 30 | x | | |
|
||||
| Second | 45 | x | | |
|
||||
| Millisecond | 678 | x | | |
|
||||
| Day (Week day) | Saturday | x | | |
|
||||
| Day of the week | 6 | x | | |
|
||||
| Day of the month | 5 | x | | |
|
||||
| Day of the year | 64 | x | | |
|
||||
| Week of the month | 1 | x | | |
|
||||
| Week of the year (Calendar week, Week number) | 10 | x | | |
|
||||
| Month | March | x | | |
|
||||
| Month of the year | 3 | x | | |
|
||||
| Month and day | March 7 | x | x | |
|
||||
| Year | 2022 | x | | |
|
||||
| Era | AD | x | | |
|
||||
| Era abbreviation | A | x | | |
|
||||
| Month and year | March 2022 | x | x | |
|
||||
| Windows file time (Int64 number) | 637820976123938199 | x | x | |
|
||||
| Universal time format: YYYY-MM-DD hh:mm:ss| 2022-03-05 16:20:12Z | x | x | |
|
||||
| ISO 8601 | 2022-03-05T17:23:04 | x | x | |
|
||||
| ISO 8601 UTC | 2022-03-05T16:23:04 | x | x | |
|
||||
| ISO 8601 with time zone | 2022-03-05T17:23:04+01:00 | x | x | |
|
||||
| ISO 8601 UTC with time zone | 2022-03-05T16:23:04Z | x | x | |
|
||||
| RFC1123 | Sat, 05 Mar 2022 16:23:04 GMT | x | x | |
|
||||
| OLE Automation date | 45723.44143763889 | | x | x |
|
||||
| Excel's 1900 date value | 45723.44143763889 | | x | x |
|
||||
| Excel's 1904 date value | 44261.44143763889 | | x | x |
|
||||
|
||||
**Custom format definition**
|
||||
The user can create its own formats. One per line in the settings text box. The format of each line is `<name>=<syntax pattern>`.
|
||||
If the syntax pattern starting with `UTC:` then we use the UTC time instead of the local time.
|
||||
As syntax pattern the pattern from `DateTime.ToString()` and the following custom pattern are available:
|
||||
- DOW: Number of the day in the week.
|
||||
- WOM: Number of week in the month.
|
||||
- WOY: Number of the week in the year.
|
||||
- EAB: Era abbreviation.
|
||||
- WFT: Windows file time.
|
||||
- UXT: Unix time stamp.
|
||||
- UMS: Unix time stamp in milliseconds.
|
||||
- OAD: OLE Automation date.
|
||||
- EXC: Excel's 1900 based date value.
|
||||
- EXF: Excel's 1904 based date value.
|
||||
|
||||
### Add new formats
|
||||
- To add a new formats you have to add them to the method `GetList()` of the [`AvailableResultsList`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/AvailableResultsList.cs) class.
|
||||
@@ -73,13 +94,13 @@ The following formats are currently available:
|
||||
|
||||
| Key | Type | Default value | Name | Description |
|
||||
|--------------|--------------|-----------|------------|------------|
|
||||
| `CalendarFirstWeekRule` | Combo box | `-1` (Use system settings) | First week of the year | Configure the calendar rule for the first week of the year. |
|
||||
| `FirstDayOfWeek` | Combo box | `-1` (Use system settings) | First day of the week | |
|
||||
| `OnlyDateTimeNowGlobal` | Checkbox | `true` | Show only 'Time', 'Date', and 'Now' result for system time on global queries | Regardless of this setting, for global queries the first word of the query has to be a complete match. |
|
||||
| `TimeWithSeconds` | Checkbox | `false` | Show time with seconds | This setting applies to the 'Time' and 'Now' result. |
|
||||
| `DateWithWeekday` | Checkbox | `false` | Show date with weekday and name of month | This setting applies to the 'Date' and 'Now' result. |
|
||||
| `HideNumberMessageOnGlobalQuery` | Checkbox | `false` | Hide 'Invalid number input' error message on global queries | |
|
||||
|
||||
| `CalendarFirstWeekRule` | Combo box | `-1` (Use system settings) | First week of the year | Configure the calendar rule for the first week of the year. |
|
||||
| `FirstDayOfWeek` | Combo box | `-1` (Use system settings) | First day of the week | |
|
||||
| `CustomFormats` | Multiline text box | `string.Empty` | Custom formats | Use date and time string format syntax and DOW (Day of Week), WOM (Week of Month), WOY (Week of the year), EAB (Era abbreviation), WFT (Windows File Time), UXT (Unix Time), UMS (Unix Time in milliseconds), OAD (OLE Automation date), EXC (Excel's 1900 based date value), EXF (Excel's 1904 based date value). If the format starts with UTC:, then Universal Time (UTC) is used. (Use a backslash to escape format sequences and the backslash character as text.) |
|
||||
|
||||
## Classes
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@
|
||||
<RegistryValue Type="string" Name="Module_CmdPal_Deps" Value="" KeyPath="yes"/>
|
||||
</RegistryKey>
|
||||
<File Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion).0_Test\Dependencies\x64\Microsoft.VCLibs.x64.14.00.Desktop.appx" />
|
||||
<File Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion).0_Test\Dependencies\x64\Microsoft.WindowsAppRuntime.1.6.msix" />
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
<?else ?>
|
||||
@@ -50,7 +49,6 @@
|
||||
<RegistryValue Type="string" Name="Module_CmdPal_Deps" Value="" KeyPath="yes"/>
|
||||
</RegistryKey>
|
||||
<File Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion).0_Test\Dependencies\arm64\Microsoft.VCLibs.ARM64.14.00.Desktop.appx" />
|
||||
<File Source="$(var.CmdPalBuildDir)AppPackages\Microsoft.CmdPal.UI_$(var.CmdPalVersion).0_Test\Dependencies\arm64\Microsoft.WindowsAppRuntime.1.6.msix" />
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
<?endif ?>
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
#include <appxpackaging.h>
|
||||
#include <exception>
|
||||
#include <filesystem>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <Shlwapi.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
#include <winrt/Windows.ApplicationModel.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
@@ -15,11 +18,12 @@
|
||||
#include "../logger/logger.h"
|
||||
#include "../version/version.h"
|
||||
|
||||
namespace package {
|
||||
|
||||
namespace package
|
||||
{
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::ApplicationModel;
|
||||
using namespace winrt::Windows::Management::Deployment;
|
||||
using Microsoft::WRL::ComPtr;
|
||||
|
||||
inline BOOL IsWin11OrGreater()
|
||||
{
|
||||
@@ -46,6 +50,118 @@ namespace package {
|
||||
dwlConditionMask);
|
||||
}
|
||||
|
||||
struct PACKAGE_VERSION
|
||||
{
|
||||
UINT16 Major;
|
||||
UINT16 Minor;
|
||||
UINT16 Build;
|
||||
UINT16 Revision;
|
||||
};
|
||||
|
||||
class ComInitializer
|
||||
{
|
||||
public:
|
||||
explicit ComInitializer(DWORD coInitFlags = COINIT_MULTITHREADED) :
|
||||
_initialized(false)
|
||||
{
|
||||
const HRESULT hr = CoInitializeEx(nullptr, coInitFlags);
|
||||
_initialized = SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
~ComInitializer()
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
CoUninitialize();
|
||||
}
|
||||
}
|
||||
|
||||
bool Succeeded() const { return _initialized; }
|
||||
|
||||
private:
|
||||
bool _initialized;
|
||||
};
|
||||
|
||||
inline bool GetPackageNameAndVersionFromAppx(
|
||||
const std::wstring& appxPath,
|
||||
std::wstring& outName,
|
||||
PACKAGE_VERSION& outVersion)
|
||||
{
|
||||
try
|
||||
{
|
||||
ComInitializer comInit;
|
||||
if (!comInit.Succeeded())
|
||||
{
|
||||
Logger::error(L"COM initialization failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
ComPtr<IAppxFactory> factory;
|
||||
ComPtr<IStream> stream;
|
||||
ComPtr<IAppxPackageReader> reader;
|
||||
ComPtr<IAppxManifestReader> manifest;
|
||||
ComPtr<IAppxManifestPackageId> packageId;
|
||||
|
||||
HRESULT hr = CoCreateInstance(__uuidof(AppxFactory), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&factory));
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
hr = SHCreateStreamOnFileEx(appxPath.c_str(), STGM_READ | STGM_SHARE_DENY_WRITE, FILE_ATTRIBUTE_NORMAL, FALSE, nullptr, &stream);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
hr = factory->CreatePackageReader(stream.Get(), &reader);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
hr = reader->GetManifest(&manifest);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
hr = manifest->GetPackageId(&packageId);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
LPWSTR name = nullptr;
|
||||
hr = packageId->GetName(&name);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
UINT64 version = 0;
|
||||
hr = packageId->GetVersion(&version);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
outName = std::wstring(name);
|
||||
CoTaskMemFree(name);
|
||||
|
||||
outVersion.Major = static_cast<UINT16>((version >> 48) & 0xFFFF);
|
||||
outVersion.Minor = static_cast<UINT16>((version >> 32) & 0xFFFF);
|
||||
outVersion.Build = static_cast<UINT16>((version >> 16) & 0xFFFF);
|
||||
outVersion.Revision = static_cast<UINT16>(version & 0xFFFF);
|
||||
|
||||
Logger::info(L"Package name: {}, version: {}.{}.{}.{}, appxPath: {}",
|
||||
outName,
|
||||
outVersion.Major,
|
||||
outVersion.Minor,
|
||||
outVersion.Build,
|
||||
outVersion.Revision,
|
||||
appxPath);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
Logger::error(L"Standard exception: {}", winrt::to_hstring(ex.what()));
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Logger::error(L"Unknown or non-standard exception occurred.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline std::optional<Package> GetRegisteredPackage(std::wstring packageDisplayName, bool checkVersion)
|
||||
{
|
||||
PackageManager packageManager;
|
||||
@@ -229,6 +345,59 @@ namespace package {
|
||||
return matchedFiles;
|
||||
}
|
||||
|
||||
inline bool IsPackageSatisfied(const std::wstring& appxPath)
|
||||
{
|
||||
std::wstring targetName;
|
||||
PACKAGE_VERSION targetVersion{};
|
||||
|
||||
if (!GetPackageNameAndVersionFromAppx(appxPath, targetName, targetVersion))
|
||||
{
|
||||
Logger::error(L"Failed to get package name and version from appx: " + appxPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
PackageManager pm;
|
||||
|
||||
for (const auto& package : pm.FindPackagesForUser({}))
|
||||
{
|
||||
const auto& id = package.Id();
|
||||
if (std::wstring(id.Name()) == targetName)
|
||||
{
|
||||
const auto& version = id.Version();
|
||||
|
||||
if (version.Major > targetVersion.Major ||
|
||||
(version.Major == targetVersion.Major && version.Minor > targetVersion.Minor) ||
|
||||
(version.Major == targetVersion.Major && version.Minor == targetVersion.Minor && version.Build > targetVersion.Build) ||
|
||||
(version.Major == targetVersion.Major && version.Minor == targetVersion.Minor && version.Build == targetVersion.Build && version.Revision >= targetVersion.Revision))
|
||||
{
|
||||
Logger::info(
|
||||
L"Package {} is already satisfied with version {}.{}.{}.{}; target version {}.{}.{}.{}; appxPath: {}",
|
||||
id.Name(),
|
||||
version.Major,
|
||||
version.Minor,
|
||||
version.Build,
|
||||
version.Revision,
|
||||
targetVersion.Major,
|
||||
targetVersion.Minor,
|
||||
targetVersion.Build,
|
||||
targetVersion.Revision,
|
||||
appxPath);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Logger::info(
|
||||
L"Package {} is not satisfied. Target version: {}.{}.{}.{}; appxPath: {}",
|
||||
targetName,
|
||||
targetVersion.Major,
|
||||
targetVersion.Minor,
|
||||
targetVersion.Build,
|
||||
targetVersion.Revision,
|
||||
appxPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool RegisterPackage(std::wstring pkgPath, std::vector<std::wstring> dependencies)
|
||||
{
|
||||
try
|
||||
@@ -238,7 +407,7 @@ namespace package {
|
||||
PackageManager packageManager;
|
||||
|
||||
// Declare use of an external location
|
||||
DeploymentOptions options = DeploymentOptions::ForceApplicationShutdown;
|
||||
DeploymentOptions options = DeploymentOptions::ForceTargetApplicationShutdown;
|
||||
|
||||
Collections::IVector<Uri> uris = winrt::single_threaded_vector<Uri>();
|
||||
if (!dependencies.empty())
|
||||
@@ -247,7 +416,14 @@ namespace package {
|
||||
{
|
||||
try
|
||||
{
|
||||
uris.Append(Uri(dependency));
|
||||
if (IsPackageSatisfied(dependency))
|
||||
{
|
||||
Logger::info(L"Dependency already satisfied: {}", dependency);
|
||||
}
|
||||
else
|
||||
{
|
||||
uris.Append(Uri(dependency));
|
||||
}
|
||||
}
|
||||
catch (const winrt::hresult_error& ex)
|
||||
{
|
||||
@@ -282,7 +458,6 @@ namespace package {
|
||||
{
|
||||
Logger::debug(L"Register {} package started.", pkgPath);
|
||||
}
|
||||
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
@@ -293,4 +468,4 @@ namespace package {
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Microsoft.CmdPal.Ext.TimeDate.Helpers;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
@@ -12,6 +14,12 @@ namespace Microsoft.CmdPal.Ext.TimeDate.Pages;
|
||||
|
||||
internal sealed partial class TimeDateExtensionPage : DynamicListPage
|
||||
{
|
||||
private readonly Lock _resultsLock = new();
|
||||
|
||||
private IList<ListItem> _results = new List<ListItem>();
|
||||
|
||||
private bool initialized;
|
||||
|
||||
private SettingsManager _settingsManager;
|
||||
|
||||
public TimeDateExtensionPage(SettingsManager settingsManager)
|
||||
@@ -24,20 +32,36 @@ internal sealed partial class TimeDateExtensionPage : DynamicListPage
|
||||
_settingsManager = settingsManager;
|
||||
}
|
||||
|
||||
public override IListItem[] GetItems() => DoExecuteSearch(SearchText).ToArray();
|
||||
public override IListItem[] GetItems()
|
||||
{
|
||||
if (!initialized)
|
||||
{
|
||||
DoExecuteSearch(string.Empty);
|
||||
}
|
||||
|
||||
lock (_resultsLock)
|
||||
{
|
||||
ListItem[] results = _results.ToArray();
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateSearchText(string oldSearch, string newSearch)
|
||||
{
|
||||
if (newSearch == oldSearch)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DoExecuteSearch(newSearch);
|
||||
RaiseItemsChanged(0);
|
||||
}
|
||||
|
||||
private List<ListItem> DoExecuteSearch(string query)
|
||||
private void DoExecuteSearch(string query)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = TimeDateCalculator.ExecuteSearch(_settingsManager, query);
|
||||
return result;
|
||||
UpdateResult(result);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
@@ -51,7 +75,18 @@ internal sealed partial class TimeDateExtensionPage : DynamicListPage
|
||||
ResultHelper.CreateInvalidInputErrorResult(),
|
||||
};
|
||||
|
||||
return items;
|
||||
UpdateResult(items);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateResult(IList<ListItem> result)
|
||||
{
|
||||
lock (_resultsLock)
|
||||
{
|
||||
initialized = true;
|
||||
this._results = result;
|
||||
}
|
||||
|
||||
RaiseItemsChanged(this._results.Count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,7 +295,7 @@
|
||||
"Areas": [ "AreaEaseOfAccess" ],
|
||||
"Type": "AppSettingsApp",
|
||||
"AltNames": [ "TouchFeedback" ],
|
||||
"Command": "ms-settings:easeofaccess-MousePointer"
|
||||
"Command": "ms-settings:easeofaccess-mousepointer"
|
||||
},
|
||||
{
|
||||
"Name": "Display",
|
||||
|
||||
@@ -16,21 +16,31 @@ public partial class SamplesListPage : ListPage
|
||||
{
|
||||
Title = "List Page Sample Command",
|
||||
Subtitle = "Display a list of items",
|
||||
Section = "Lists",
|
||||
},
|
||||
new ListItem(new SampleListPageWithDetails())
|
||||
{
|
||||
Title = "List Page With Details",
|
||||
Subtitle = "A list of items, each with additional details to display",
|
||||
Section = "Lists",
|
||||
},
|
||||
new ListItem(new SampleUpdatingItemsPage())
|
||||
{
|
||||
Title = "List page with items that change",
|
||||
Subtitle = "The items on the list update themselves in real time",
|
||||
Section = "Lists",
|
||||
},
|
||||
new ListItem(new SampleDynamicListPage())
|
||||
{
|
||||
Title = "Dynamic List Page Command",
|
||||
Subtitle = "Changes the list of items in response to the typed query",
|
||||
Section = "Lists",
|
||||
},
|
||||
new ListItem(new FizzBuzzListPage())
|
||||
{
|
||||
Title = "Sections sample",
|
||||
Subtitle = "Changing list of items, with sections",
|
||||
Section = "Lists",
|
||||
},
|
||||
|
||||
// Content pages
|
||||
@@ -38,32 +48,38 @@ public partial class SamplesListPage : ListPage
|
||||
{
|
||||
Title = "Sample content page",
|
||||
Subtitle = "Display mixed forms, markdown, and other types of content",
|
||||
Section = "Content",
|
||||
},
|
||||
new ListItem(new SampleTreeContentPage())
|
||||
{
|
||||
Title = "Sample nested content",
|
||||
Subtitle = "Example of nesting a tree of content",
|
||||
Section = "Content",
|
||||
},
|
||||
new ListItem(new SampleCommentsPage())
|
||||
{
|
||||
Title = "Sample of nested comments",
|
||||
Subtitle = "Demo of using nested trees of content to create a comment thread-like experience",
|
||||
Icon = new IconInfo("\uE90A"), // Comment
|
||||
Section = "Content",
|
||||
},
|
||||
new ListItem(new SampleMarkdownPage())
|
||||
{
|
||||
Title = "Markdown Page Sample Command",
|
||||
Subtitle = "Display a page of rendered markdown",
|
||||
Section = "Content",
|
||||
},
|
||||
new ListItem(new SampleMarkdownManyBodies())
|
||||
{
|
||||
Title = "Markdown with multiple blocks",
|
||||
Subtitle = "A page with multiple blocks of rendered markdown",
|
||||
Section = "Content",
|
||||
},
|
||||
new ListItem(new SampleMarkdownDetails())
|
||||
{
|
||||
Title = "Markdown with details",
|
||||
Subtitle = "A page with markdown and details",
|
||||
Section = "Content",
|
||||
},
|
||||
|
||||
// Settings helpers
|
||||
@@ -71,6 +87,7 @@ public partial class SamplesListPage : ListPage
|
||||
{
|
||||
Title = "Sample settings page",
|
||||
Subtitle = "A demo of the settings helpers",
|
||||
Section = "Settings",
|
||||
},
|
||||
|
||||
// Evil edge cases
|
||||
@@ -79,6 +96,7 @@ public partial class SamplesListPage : ListPage
|
||||
{
|
||||
Title = "Evil samples",
|
||||
Subtitle = "Samples designed to break the palette in many different evil ways",
|
||||
Section = "Debugging",
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -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.Collections.ObjectModel;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace Microsoft.CmdPal.UI.ViewModels;
|
||||
|
||||
public partial class ListGroup : ObservableObject
|
||||
{
|
||||
public string Key { get; set; } = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<ListItemViewModel> Items { get; set; } = [];
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
@@ -27,6 +27,12 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
|
||||
private ObservableCollection<ListItemViewModel> Items { get; set; } = [];
|
||||
|
||||
[ObservableProperty]
|
||||
public partial bool HasGrouping { get; private set; } = false;
|
||||
|
||||
[ObservableProperty]
|
||||
public partial ObservableCollection<ListGroup> Groups { get; set; } = [];
|
||||
|
||||
private readonly ExtensionObject<IListPage> _model;
|
||||
|
||||
private readonly Lock _listLock = new();
|
||||
@@ -248,6 +254,53 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateGroupsIfNeeded()
|
||||
{
|
||||
HasGrouping = FilteredItems.Any(i => !string.IsNullOrEmpty(i.Section));
|
||||
|
||||
if (HasGrouping)
|
||||
{
|
||||
lock (_listLock)
|
||||
{
|
||||
if (FilteredItems.Count == 0)
|
||||
{
|
||||
Groups.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// get current groups
|
||||
var groups = FilteredItems.GroupBy(item => item.Section).Select(group => group);
|
||||
|
||||
// Remove any groups that no longer exist
|
||||
foreach (var group in Groups)
|
||||
{
|
||||
if (!groups.Any(g => g.Key == group.Key))
|
||||
{
|
||||
Groups.Remove(group);
|
||||
}
|
||||
}
|
||||
|
||||
// Update lists for each existing group
|
||||
foreach (var group in groups)
|
||||
{
|
||||
var existingGroup = Groups.FirstOrDefault(groupItem => groupItem.Key == group.Key);
|
||||
if (existingGroup == null)
|
||||
{
|
||||
// Add a new group if it doesn't exist
|
||||
Groups.Add(new ListGroup { Key = group.Key, Items = new ObservableCollection<ListItemViewModel>(group) });
|
||||
existingGroup = Groups.FirstOrDefault(groupItem => groupItem.Key == group.Key);
|
||||
}
|
||||
|
||||
if (existingGroup != null)
|
||||
{
|
||||
// Update the existing group
|
||||
ListHelpers.InPlaceUpdateList(existingGroup.Items, FilteredItems.Where(item => item.Section == group.Key));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply our current filter text to the list of items, and update
|
||||
/// FilteredItems to match the results.
|
||||
@@ -373,9 +426,9 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
_modelPlaceholderText = model.PlaceholderText;
|
||||
UpdateProperty(nameof(PlaceholderText));
|
||||
|
||||
// SearchText = model.SearchText;
|
||||
SearchText = string.Empty;
|
||||
SearchText = model.SearchText;
|
||||
UpdateProperty(nameof(SearchText));
|
||||
|
||||
EmptyContent = new(new(model.EmptyContent), PageContext);
|
||||
EmptyContent.SlowInitializeProperties();
|
||||
|
||||
@@ -487,6 +540,18 @@ public partial class ListViewModel : PageViewModel, IDisposable
|
||||
}
|
||||
|
||||
FilteredItems.Clear();
|
||||
|
||||
foreach (ListGroup group in Groups)
|
||||
{
|
||||
foreach (ListItemViewModel item in group.Items)
|
||||
{
|
||||
item.SafeCleanup();
|
||||
}
|
||||
|
||||
group.Items.Clear();
|
||||
}
|
||||
|
||||
Groups.Clear();
|
||||
}
|
||||
|
||||
IListPage? model = _model.Unsafe;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<!-- Other merged dictionaries here -->
|
||||
<ResourceDictionary Source="Styles/Button.xaml" />
|
||||
<ResourceDictionary Source="Styles/Colors.xaml" />
|
||||
<ResourceDictionary Source="Styles/Generic.xaml" />
|
||||
<ResourceDictionary Source="Styles/TextBox.xaml" />
|
||||
<ResourceDictionary Source="Styles/Settings.xaml" />
|
||||
<ResourceDictionary Source="Controls/Tag.xaml" />
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// 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 Microsoft.CmdPal.Common.Helpers;
|
||||
using Microsoft.CmdPal.Common.Services;
|
||||
using Microsoft.CmdPal.Ext.Apps;
|
||||
@@ -78,7 +79,7 @@ public partial class App : Application
|
||||
|
||||
var cmdArgs = Environment.GetCommandLineArgs();
|
||||
|
||||
bool runFromPT = false;
|
||||
var runFromPT = false;
|
||||
foreach (var arg in cmdArgs)
|
||||
{
|
||||
if (arg == "RunFromPT")
|
||||
@@ -107,9 +108,6 @@ public partial class App : Application
|
||||
|
||||
// Built-in Commands. Order matters - this is the order they'll be presented by default.
|
||||
var allApps = new AllAppsCommandProvider();
|
||||
var winget = new WinGetExtensionCommandsProvider();
|
||||
var callback = allApps.LookupApp;
|
||||
winget.SetAllLookup(callback);
|
||||
services.AddSingleton<ICommandProvider>(allApps);
|
||||
services.AddSingleton<ICommandProvider, ShellCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, CalculatorCommandProvider>();
|
||||
@@ -120,7 +118,25 @@ public partial class App : Application
|
||||
// services.AddSingleton<ICommandProvider, ClipboardHistoryCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, WindowWalkerCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, WebSearchCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider>(winget);
|
||||
|
||||
// GH #38440: Users might not have WinGet installed! Or they might have
|
||||
// a ridiculously old version. Or might be running as admin.
|
||||
// We shouldn't explode in the App ctor if we fail to instantiate an
|
||||
// instance of PackageManager, which will happen in the static ctor
|
||||
// for WinGetStatics
|
||||
try
|
||||
{
|
||||
var winget = new WinGetExtensionCommandsProvider();
|
||||
var callback = allApps.LookupApp;
|
||||
winget.SetAllLookup(callback);
|
||||
services.AddSingleton<ICommandProvider>(winget);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Couldn't load winget");
|
||||
Logger.LogError(ex.ToString());
|
||||
}
|
||||
|
||||
services.AddSingleton<ICommandProvider, WindowsTerminalCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, WindowsSettingsCommandsProvider>();
|
||||
services.AddSingleton<ICommandProvider, RegistryCommandsProvider>();
|
||||
|
||||
@@ -105,19 +105,8 @@ public sealed partial class SearchBar : UserControl,
|
||||
|
||||
var ctrlPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Control).HasFlag(CoreVirtualKeyStates.Down);
|
||||
var altPressed = InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Menu).HasFlag(CoreVirtualKeyStates.Down);
|
||||
if (e.Key == VirtualKey.Down)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send<NavigateNextCommand>();
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == VirtualKey.Up)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send<NavigatePreviousCommand>();
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (ctrlPressed && e.Key == VirtualKey.Enter)
|
||||
if (ctrlPressed && e.Key == VirtualKey.Enter)
|
||||
{
|
||||
// ctrl+enter
|
||||
WeakReferenceMessenger.Default.Send<ActivateSecondaryCommandMessage>();
|
||||
@@ -197,6 +186,18 @@ public sealed partial class SearchBar : UserControl,
|
||||
_isBackspaceHeld = true;
|
||||
}
|
||||
}
|
||||
else if (e.Key == VirtualKey.Up)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send<NavigatePreviousCommand>();
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == VirtualKey.Down)
|
||||
{
|
||||
WeakReferenceMessenger.Default.Send<NavigateNextCommand>();
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void FilterBox_PreviewKeyUp(object sender, KeyRoutedEventArgs e)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Page
|
||||
x:Class="Microsoft.CmdPal.UI.ListPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
@@ -17,10 +17,16 @@
|
||||
<Page.Resources>
|
||||
<!-- TODO: Figure out what we want to do here for filtering/grouping and where -->
|
||||
<!-- https://learn.microsoft.com/windows/windows-app-sdk/api/winrt/microsoft.ui.xaml.data.collectionviewsource -->
|
||||
<!--<CollectionViewSource
|
||||
x:Name="ItemsCVS"
|
||||
<CollectionViewSource
|
||||
x:Name="GroupedItemsCVS"
|
||||
IsSourceGrouped="True"
|
||||
Source="{x:Bind ViewModel.Items, Mode=OneWay}" />-->
|
||||
ItemsPath="Items"
|
||||
Source="{x:Bind ViewModel.Groups, Mode=OneWay}" />
|
||||
|
||||
<CollectionViewSource
|
||||
x:Name="UngroupedItemsCVS"
|
||||
IsSourceGrouped="False"
|
||||
Source="{x:Bind ViewModel.FilteredItems, Mode=OneWay}" />
|
||||
|
||||
<converters:StringVisibilityConverter
|
||||
x:Key="StringVisibilityConverter"
|
||||
@@ -107,32 +113,74 @@
|
||||
TargetType="x:Boolean"
|
||||
Value="{x:Bind ViewModel.ShowEmptyContent, Mode=OneWay}">
|
||||
<controls:Case Value="False">
|
||||
<ListView
|
||||
x:Name="ItemsList"
|
||||
Padding="0,2,0,0"
|
||||
DoubleTapped="ItemsList_DoubleTapped"
|
||||
IsDoubleTapEnabled="True"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="ItemsList_ItemClick"
|
||||
ItemTemplate="{StaticResource ListItemViewModelTemplate}"
|
||||
ItemsSource="{x:Bind ViewModel.FilteredItems, Mode=OneWay}"
|
||||
SelectionChanged="ItemsList_SelectionChanged">
|
||||
<ListView.ItemContainerTransitions>
|
||||
<TransitionCollection />
|
||||
</ListView.ItemContainerTransitions>
|
||||
<!--<ListView.GroupStyle>
|
||||
<GroupStyle HidesIfEmpty="True">
|
||||
<GroupStyle.HeaderTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock
|
||||
Margin="0,16,0,0"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="{Binding Key, Mode=OneWay}" />
|
||||
</DataTemplate>
|
||||
</GroupStyle.HeaderTemplate>
|
||||
</GroupStyle>
|
||||
</ListView.GroupStyle>-->
|
||||
</ListView>
|
||||
<controls:SwitchPresenter
|
||||
HorizontalAlignment="Stretch"
|
||||
TargetType="x:Boolean"
|
||||
Value="{x:Bind ViewModel.HasGrouping, Mode=OneWay}">
|
||||
<controls:Case Value="True">
|
||||
<ListView
|
||||
x:Name="GroupedItemsList"
|
||||
Padding="0,2,0,0"
|
||||
DoubleTapped="GroupedItemsList_DoubleTapped"
|
||||
IsDoubleTapEnabled="True"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="ItemsList_ItemClick"
|
||||
ItemTemplate="{StaticResource ListItemViewModelTemplate}"
|
||||
ItemsSource="{Binding ElementName=GroupedItemsCVS, Path=View}"
|
||||
SelectionChanged="GroupedItemsList_SelectionChanged">
|
||||
<ListView.ItemContainerTransitions>
|
||||
<TransitionCollection />
|
||||
</ListView.ItemContainerTransitions>
|
||||
<ListView.GroupStyle>
|
||||
<!--<GroupStyle HeaderContainerStyle="{StaticResource CustomHeaderContainerStyle}" HidesIfEmpty="True">
|
||||
<GroupStyle.HeaderTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock
|
||||
Padding="20,12,0,8"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Text="{x:Bind Key}"
|
||||
Visibility="{x:Bind Key, Converter={StaticResource StringNotEmptyToVisibilityConverter}, Mode=OneWay}" />
|
||||
</DataTemplate>
|
||||
</GroupStyle.HeaderTemplate>
|
||||
</GroupStyle>-->
|
||||
|
||||
<GroupStyle HeaderContainerStyle="{StaticResource CustomHeaderContainerStyle}" HidesIfEmpty="True">
|
||||
<GroupStyle.HeaderTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock
|
||||
Margin="0,16,0,0"
|
||||
Padding="20,8,0,4"
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="{Binding Key, Mode=OneWay}"
|
||||
Visibility="{Binding Key, Converter={StaticResource StringVisibilityConverter}}" />
|
||||
</DataTemplate>
|
||||
</GroupStyle.HeaderTemplate>
|
||||
</GroupStyle>
|
||||
</ListView.GroupStyle>
|
||||
</ListView>
|
||||
</controls:Case>
|
||||
<controls:Case Value="False">
|
||||
<ListView
|
||||
x:Name="ItemsList"
|
||||
Padding="0,2,0,0"
|
||||
DoubleTapped="ItemsList_DoubleTapped"
|
||||
IsDoubleTapEnabled="True"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="ItemsList_ItemClick"
|
||||
ItemTemplate="{StaticResource ListItemViewModelTemplate}"
|
||||
ItemsSource="{Binding ElementName=UngroupedItemsCVS, Path=View}"
|
||||
SelectionChanged="ItemsList_SelectionChanged">
|
||||
<ListView.ItemContainerTransitions>
|
||||
<TransitionCollection />
|
||||
</ListView.ItemContainerTransitions>
|
||||
</ListView>
|
||||
</controls:Case>
|
||||
</controls:SwitchPresenter>
|
||||
</controls:Case>
|
||||
<controls:Case Value="True">
|
||||
<StackPanel
|
||||
|
||||
@@ -37,6 +37,7 @@ public sealed partial class ListPage : Page,
|
||||
this.InitializeComponent();
|
||||
this.NavigationCacheMode = NavigationCacheMode.Disabled;
|
||||
this.ItemsList.Loaded += ItemsList_Loaded;
|
||||
this.GroupedItemsList.Loaded += GroupedItemsList_Loaded;
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
@@ -121,6 +122,18 @@ public sealed partial class ListPage : Page,
|
||||
}
|
||||
}
|
||||
|
||||
private void GroupedItemsList_DoubleTapped(object sender, DoubleTappedRoutedEventArgs e)
|
||||
{
|
||||
if (GroupedItemsList.SelectedItem is ListItemViewModel vm)
|
||||
{
|
||||
var settings = App.Current.Services.GetService<SettingsModel>()!;
|
||||
if (!settings.SingleClickActivates)
|
||||
{
|
||||
ViewModel?.InvokeItemCommand.Execute(vm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "VS is too aggressive at pruning methods bound in XAML")]
|
||||
private void ItemsList_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
@@ -148,6 +161,24 @@ public sealed partial class ListPage : Page,
|
||||
}
|
||||
}
|
||||
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "VS is too aggressive at pruning methods bound in XAML")]
|
||||
private void GroupedItemsList_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (GroupedItemsList.SelectedItem is ListItemViewModel item)
|
||||
{
|
||||
var vm = ViewModel;
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
vm?.UpdateSelectedItemCommand.Execute(item);
|
||||
});
|
||||
}
|
||||
|
||||
if (GroupedItemsList.SelectedItem != null)
|
||||
{
|
||||
GroupedItemsList.ScrollIntoView(GroupedItemsList.SelectedItem);
|
||||
}
|
||||
}
|
||||
|
||||
private void ItemsList_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Find the ScrollViewer in the ListView
|
||||
@@ -159,6 +190,17 @@ public sealed partial class ListPage : Page,
|
||||
}
|
||||
}
|
||||
|
||||
private void GroupedItemsList_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Find the ScrollViewer in the ListView
|
||||
var listViewScrollViewer = FindScrollViewer(this.GroupedItemsList);
|
||||
|
||||
if (listViewScrollViewer != null)
|
||||
{
|
||||
listViewScrollViewer.ViewChanged += ListViewScrollViewer_ViewChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void ListViewScrollViewer_ViewChanged(object? sender, ScrollViewerViewChangedEventArgs e)
|
||||
{
|
||||
var scrollView = sender as ScrollViewer;
|
||||
@@ -187,6 +229,14 @@ public sealed partial class ListPage : Page,
|
||||
{
|
||||
ItemsList.SelectedIndex++;
|
||||
}
|
||||
else if (GroupedItemsList.SelectedIndex < GroupedItemsList.Items.Count - 1)
|
||||
{
|
||||
GroupedItemsList.SelectedIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ItemsList.SelectedIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(NavigatePreviousCommand message)
|
||||
@@ -195,6 +245,11 @@ public sealed partial class ListPage : Page,
|
||||
{
|
||||
ItemsList.SelectedIndex--;
|
||||
}
|
||||
|
||||
if (GroupedItemsList.SelectedIndex > 0)
|
||||
{
|
||||
GroupedItemsList.SelectedIndex--;
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(ActivateSelectedListItemMessage message)
|
||||
@@ -207,6 +262,10 @@ public sealed partial class ListPage : Page,
|
||||
{
|
||||
ViewModel?.InvokeItemCommand.Execute(item);
|
||||
}
|
||||
else if (GroupedItemsList.SelectedItem is ListItemViewModel groupedItem)
|
||||
{
|
||||
ViewModel?.InvokeItemCommand.Execute(groupedItem);
|
||||
}
|
||||
}
|
||||
|
||||
public void Receive(ActivateSecondaryCommandMessage message)
|
||||
@@ -219,6 +278,10 @@ public sealed partial class ListPage : Page,
|
||||
{
|
||||
ViewModel?.InvokeSecondaryCommandCommand.Execute(item);
|
||||
}
|
||||
else if (GroupedItemsList.SelectedItem is ListItemViewModel groupedItem)
|
||||
{
|
||||
ViewModel?.InvokeSecondaryCommandCommand.Execute(groupedItem);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnViewModelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
@@ -247,6 +310,11 @@ public sealed partial class ListPage : Page,
|
||||
// GetItems or a change in the filter.
|
||||
private void Page_ItemsUpdated(ListViewModel sender, object args)
|
||||
{
|
||||
if (ViewModel != null)
|
||||
{
|
||||
ViewModel?.UpdateGroupsIfNeeded();
|
||||
}
|
||||
|
||||
// If for some reason, we don't have a selected item, fix that.
|
||||
//
|
||||
// It's important to do this here, because once there's no selection
|
||||
@@ -258,6 +326,11 @@ public sealed partial class ListPage : Page,
|
||||
{
|
||||
ItemsList.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
if (GroupedItemsList.SelectedItem == null)
|
||||
{
|
||||
GroupedItemsList.SelectedIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void ViewModel_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<EnableMsixTooling>true</EnableMsixTooling>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
|
||||
|
||||
<Version>$(CmdPalVersion)</Version>
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ SetFocus
|
||||
SetActiveWindow
|
||||
MonitorFromWindow
|
||||
GetMonitorInfo
|
||||
GetDpiForMonitor
|
||||
SHCreateStreamOnFileEx
|
||||
CoAllowSetForegroundWindow
|
||||
SHCreateStreamOnFileEx
|
||||
|
||||
29
src/modules/cmdpal/Microsoft.CmdPal.UI/Styles/Generic.xaml
Normal file
29
src/modules/cmdpal/Microsoft.CmdPal.UI/Styles/Generic.xaml
Normal file
@@ -0,0 +1,29 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
<!-- Using custom style to remove header divider from list view headers -->
|
||||
<Style x:Key="CustomHeaderContainerStyle" TargetType="ListViewHeaderItem">
|
||||
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||
<Setter Property="FontSize" Value="{ThemeResource ListViewHeaderItemThemeFontSize}" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Margin" Value="0,0,0,0" />
|
||||
<Setter Property="Padding" Value="12,8,12,0" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Left" />
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
<Setter Property="MinHeight" Value="0" />
|
||||
<Setter Property="UseSystemFocusVisuals" Value="True" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ListViewHeaderItem">
|
||||
<ContentPresenter
|
||||
x:Name="ContentPresenter"
|
||||
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
|
||||
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
|
||||
Background="{TemplateBinding Background}"
|
||||
Content="{TemplateBinding Content}"
|
||||
ContentTemplate="{TemplateBinding ContentTemplate}"
|
||||
ContentTransitions="{TemplateBinding ContentTransitions}" />
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.WinUI;
|
||||
using ManagedCommon;
|
||||
using Microsoft.CmdPal.UI.Helpers;
|
||||
using Microsoft.CmdPal.UI.ViewModels;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Messages;
|
||||
@@ -13,6 +14,8 @@ using Microsoft.UI.Xaml;
|
||||
using Windows.Graphics;
|
||||
using Windows.Win32;
|
||||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Graphics.Gdi;
|
||||
using Windows.Win32.UI.HiDpi;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
using RS_ = Microsoft.CmdPal.UI.Helpers.ResourceLoaderInstance;
|
||||
|
||||
@@ -44,6 +47,21 @@ public sealed partial class ToastWindow : Window,
|
||||
WeakReferenceMessenger.Default.Register<QuitMessage>(this);
|
||||
}
|
||||
|
||||
private static double GetScaleFactor(HWND hwnd)
|
||||
{
|
||||
try
|
||||
{
|
||||
var monitor = PInvoke.MonitorFromWindow(hwnd, MONITOR_FROM_FLAGS.MONITOR_DEFAULTTONEAREST);
|
||||
_ = PInvoke.GetDpiForMonitor(monitor, MONITOR_DPI_TYPE.MDT_EFFECTIVE_DPI, out var dpiX, out _);
|
||||
return dpiX / 96.0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError($"Failed to get scale factor, error: {ex.Message}");
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
private void PositionCentered()
|
||||
{
|
||||
var intSize = new SizeInt32
|
||||
@@ -51,7 +69,14 @@ public sealed partial class ToastWindow : Window,
|
||||
Width = Convert.ToInt32(ToastText.ActualWidth),
|
||||
Height = Convert.ToInt32(ToastText.ActualHeight),
|
||||
};
|
||||
AppWindow.Resize(intSize);
|
||||
|
||||
var scaleAdjustment = GetScaleFactor(_hwnd);
|
||||
var scaled = new SizeInt32
|
||||
{
|
||||
Width = (int)Math.Round(intSize.Width * scaleAdjustment),
|
||||
Height = (int)Math.Round(intSize.Height * scaleAdjustment),
|
||||
};
|
||||
AppWindow.Resize(scaled);
|
||||
|
||||
var displayArea = DisplayArea.GetFromWindowId(AppWindow.Id, DisplayAreaFallback.Nearest);
|
||||
if (displayArea is not null)
|
||||
|
||||
@@ -189,7 +189,7 @@ public partial class InstallPackageListItem : ListItem
|
||||
|
||||
private void InstallStateChangedHandler(object? sender, InstallPackageCommand e)
|
||||
{
|
||||
if (!ApiInformation.IsApiContractPresent("Microsoft.Management.Deployment", 12))
|
||||
if (!ApiInformation.IsApiContractPresent("Microsoft.Management.Deployment.WindowsPackageManagerContract", 12))
|
||||
{
|
||||
Logger.LogError($"RefreshPackageCatalogAsync isn't available");
|
||||
e.FakeChangeStatus();
|
||||
|
||||
@@ -12,6 +12,7 @@ using System.Threading.Tasks;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Microsoft.Management.Deployment;
|
||||
using Windows.Foundation.Metadata;
|
||||
using WindowsPackageManager.Interop;
|
||||
|
||||
namespace Microsoft.CmdPal.Ext.WinGet;
|
||||
@@ -51,9 +52,12 @@ internal static class WinGetStatics
|
||||
_storeCatalog,
|
||||
];
|
||||
|
||||
foreach (var catalogReference in AvailableCatalogs)
|
||||
if (ApiInformation.IsApiContractPresent("Microsoft.Management.Deployment.WindowsPackageManagerContract", 8))
|
||||
{
|
||||
catalogReference.PackageCatalogBackgroundUpdateInterval = new(0);
|
||||
foreach (var catalogReference in AvailableCatalogs)
|
||||
{
|
||||
catalogReference.PackageCatalogBackgroundUpdateInterval = new(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Immediately start the lazy-init of the all packages catalog, but
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.CommandPalette.Extensions;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
|
||||
namespace SamplePagesExtension;
|
||||
|
||||
internal sealed partial class FizzBuzzListPage : ListPage
|
||||
{
|
||||
public override string Title => "FizzBuzz Page";
|
||||
|
||||
public override IconInfo Icon => new("\uE94C"); // % symbol
|
||||
|
||||
public override string Name => "Open";
|
||||
|
||||
private readonly List<ListItem> _items;
|
||||
|
||||
internal FizzBuzzListPage()
|
||||
{
|
||||
var addNewItem = new ListItem(new AnonymousCommand(() =>
|
||||
{
|
||||
var c = _items.Count;
|
||||
var f = c % 3 == 0;
|
||||
var b = c % 5 == 0;
|
||||
var s = string.Empty;
|
||||
if (f)
|
||||
{
|
||||
s += "Fizz";
|
||||
}
|
||||
|
||||
if (b)
|
||||
{
|
||||
s += "Buzz";
|
||||
}
|
||||
|
||||
_items.Add(new ListItem(new NoOpCommand())
|
||||
{
|
||||
Title = $"{c}",
|
||||
Icon = IconFromIndex(_items.Count),
|
||||
Section = s,
|
||||
});
|
||||
RaiseItemsChanged();
|
||||
})
|
||||
{ Result = CommandResult.KeepOpen() })
|
||||
{
|
||||
Title = "Add item",
|
||||
Subtitle = "Each item will be sorted into sections. Add at least three",
|
||||
Icon = new IconInfo("\uED0E"),
|
||||
};
|
||||
|
||||
_items = [addNewItem];
|
||||
}
|
||||
|
||||
public override IListItem[] GetItems()
|
||||
{
|
||||
return _items.ToArray();
|
||||
}
|
||||
|
||||
private IconInfo IconFromIndex(int index)
|
||||
{
|
||||
return _icons[index % _icons.Length];
|
||||
}
|
||||
|
||||
private readonly IconInfo[] _icons =
|
||||
[
|
||||
new IconInfo("\ue700"),
|
||||
new IconInfo("\ue701"),
|
||||
new IconInfo("\ue702"),
|
||||
new IconInfo("\ue703"),
|
||||
new IconInfo("\ue704"),
|
||||
new IconInfo("\ue705"),
|
||||
new IconInfo("\ue706"),
|
||||
new IconInfo("\ue707"),
|
||||
new IconInfo("\ue708"),
|
||||
new IconInfo("\ue709"),
|
||||
new IconInfo("\ue70a"),
|
||||
new IconInfo("\ue70b"),
|
||||
new IconInfo("\ue70c"),
|
||||
new IconInfo("\ue70d"),
|
||||
new IconInfo("\ue70e"),
|
||||
new IconInfo("\ue70f"),
|
||||
new IconInfo("\ue710"),
|
||||
new IconInfo("\ue711"),
|
||||
new IconInfo("\ue712"),
|
||||
new IconInfo("\ue713"),
|
||||
];
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
||||
using Microsoft.PowerToys.Run.Plugin.TimeDate.Components;
|
||||
@@ -23,7 +24,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests
|
||||
var result = settings?.Length;
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(6, result);
|
||||
Assert.AreEqual(7, result);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
@@ -33,6 +34,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests
|
||||
[DataRow("TimeWithSeconds")]
|
||||
[DataRow("DateWithWeekday")]
|
||||
[DataRow("HideNumberMessageOnGlobalQuery")]
|
||||
[DataRow("CustomFormats")]
|
||||
public void DoesSettingExist(string name)
|
||||
{
|
||||
// Setup
|
||||
@@ -78,5 +80,20 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests
|
||||
// Assert
|
||||
Assert.AreEqual(valueExpected, result);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("CustomFormats")]
|
||||
public void DefaultEmptyMultilineTextValues(string name)
|
||||
{
|
||||
// Setup
|
||||
TimeDateSettings setting = TimeDateSettings.Instance;
|
||||
|
||||
// Act
|
||||
PropertyInfo propertyInfo = setting?.GetType()?.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
List<string> result = (List<string>)propertyInfo?.GetValue(setting);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(0, result.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,11 +54,11 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("(time", 18)]
|
||||
[DataRow("(date", 26)]
|
||||
[DataRow("(year", 7)]
|
||||
[DataRow("(now", 32)]
|
||||
[DataRow("(current", 32)]
|
||||
[DataRow("(", 32)]
|
||||
[DataRow("(date", 28)]
|
||||
[DataRow("(year", 8)]
|
||||
[DataRow("(now", 34)]
|
||||
[DataRow("(current", 34)]
|
||||
[DataRow("(", 34)]
|
||||
[DataRow("(now::10:10:10", 1)] // Windows file time
|
||||
[DataRow("(current::10:10:10", 0)]
|
||||
public void CountWithPluginKeyword(string typedString, int expectedResultCount)
|
||||
@@ -140,6 +140,8 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests
|
||||
[DataRow("(week day", "Day of the week (Week day) -")]
|
||||
[DataRow("(cal week", "Week of the year (Calendar week, Week number) -")]
|
||||
[DataRow("(week num", "Week of the year (Calendar week, Week number) -")]
|
||||
[DataRow("(days in mo", "Days in month -")]
|
||||
[DataRow("(Leap y", "Leap year -")]
|
||||
public void CanFindFormatResult(string typedString, string expectedResult)
|
||||
{
|
||||
// Setup
|
||||
|
||||
@@ -41,10 +41,29 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests
|
||||
[DataRow("ums-10456", true, "", "")] // Value is UTC and can be different based on system
|
||||
[DataRow("ums+10456", true, "", "")] // Value is UTC and can be different based on system
|
||||
[DataRow("ft10456", true, "", "")] // Value is UTC and can be different based on system
|
||||
[DataRow("oa-657434.99999999", true, "G", "1/1/0100 11:59:59 PM")]
|
||||
[DataRow("oa2958465.99999999", true, "G", "12/31/9999 11:59:59 PM")]
|
||||
[DataRow("oa-657435", false, "", "")] // Value to low
|
||||
[DataRow("oa2958466", false, "", "")] // Value to large
|
||||
[DataRow("exc1.99998843", true, "G", "1/1/1900 11:59:59 PM")]
|
||||
[DataRow("exc59.99998843", true, "G", "2/28/1900 11:59:59 PM")]
|
||||
[DataRow("exc61", true, "G", "3/1/1900 12:00:00 AM")]
|
||||
[DataRow("exc62.99998843", true, "G", "3/2/1900 11:59:59 PM")]
|
||||
[DataRow("exc2958465.99998843", true, "G", "12/31/9999 11:59:59 PM")]
|
||||
[DataRow("exc0", false, "", "")] // Day 0 means in Excel 0/1/1900 and this is a fake date.
|
||||
[DataRow("exc0.99998843", false, "", "")] // Day 0 means in Excel 0/1/1900 and this is a fake date.
|
||||
[DataRow("exc60.99998843", false, "", "")] // Day 60 means in Excel 2/29/1900 and this is a fake date in Excel which we cannot support.
|
||||
[DataRow("exc60", false, "", "")] // Day 60 means in Excel 2/29/1900 and this is a fake date in Excel which we cannot support.
|
||||
[DataRow("exc-1", false, "", "")] // Value to low
|
||||
[DataRow("exc2958466", false, "", "")] // Value to large
|
||||
[DataRow("exf0.99998843", true, "G", "1/1/1904 11:59:59 PM")]
|
||||
[DataRow("exf2957003.99998843", true, "G", "12/31/9999 11:59:59 PM")]
|
||||
[DataRow("exf-0.5", false, "", "")] // Value to low
|
||||
[DataRow("exf2957004", false, "", "")] // Value to large
|
||||
public void ConvertStringToDateTime(string typedString, bool expectedBool, string stringType, string expectedString)
|
||||
{
|
||||
// Act
|
||||
bool boolResult = TimeAndDateHelper.ParseStringAsDateTime(in typedString, out DateTime result);
|
||||
bool boolResult = TimeAndDateHelper.ParseStringAsDateTime(in typedString, out DateTime result, out string _);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(expectedBool, boolResult);
|
||||
|
||||
@@ -13,6 +13,19 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests
|
||||
[TestClass]
|
||||
public class TimeAndDateHelperTests
|
||||
{
|
||||
private CultureInfo originalCulture;
|
||||
private CultureInfo originalUiCulture;
|
||||
|
||||
[TestInitialize]
|
||||
public void Setup()
|
||||
{
|
||||
// Set culture to 'en-us'
|
||||
originalCulture = CultureInfo.CurrentCulture;
|
||||
CultureInfo.CurrentCulture = new CultureInfo("en-us", false);
|
||||
originalUiCulture = CultureInfo.CurrentUICulture;
|
||||
CultureInfo.CurrentUICulture = new CultureInfo("en-us", false);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(-1, null)] // default setting
|
||||
[DataRow(0, CalendarWeekRule.FirstDay)]
|
||||
@@ -62,5 +75,103 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests
|
||||
Assert.AreEqual(valueExpected, result);
|
||||
}
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow(0, "12/30/1899 12:00 PM", 0.5)] // OLE Automation date
|
||||
[DataRow(1, "12/31/1898 12:00 PM", null)] // Excel based 1900: Date to low
|
||||
[DataRow(1, "1/1/1900, 00:00 AM", 1.0)] // Excel based 1900
|
||||
[DataRow(2, "12/31/1898 12:00 PM", null)] // Excel based 1904: Date to low
|
||||
[DataRow(2, "1/1/1904, 00:00 AM", 0.0)] // Excel based 1904
|
||||
public void ConvertToOADateFormat(int type, string date, double? valueExpected)
|
||||
{
|
||||
// Act
|
||||
DateTime dt = DateTime.Parse(date, DateTimeFormatInfo.CurrentInfo);
|
||||
|
||||
// Assert
|
||||
if (valueExpected == null)
|
||||
{
|
||||
Assert.ThrowsException<ArgumentOutOfRangeException>(() => TimeAndDateHelper.ConvertToOleAutomationFormat(dt, (OADateFormats)type));
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = TimeAndDateHelper.ConvertToOleAutomationFormat(dt, (OADateFormats)type);
|
||||
Assert.AreEqual(valueExpected, result);
|
||||
}
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("dow")]
|
||||
[DataRow("\\DOW")]
|
||||
[DataRow("wom")]
|
||||
[DataRow("\\WOM")]
|
||||
[DataRow("woy")]
|
||||
[DataRow("\\WOY")]
|
||||
[DataRow("eab")]
|
||||
[DataRow("\\EAB")]
|
||||
[DataRow("wft")]
|
||||
[DataRow("\\WFT")]
|
||||
[DataRow("uxt")]
|
||||
[DataRow("\\UXT")]
|
||||
[DataRow("ums")]
|
||||
[DataRow("\\UMS")]
|
||||
[DataRow("oad")]
|
||||
[DataRow("\\OAD")]
|
||||
[DataRow("exc")]
|
||||
[DataRow("\\EXC")]
|
||||
[DataRow("exf")]
|
||||
[DataRow("\\EXF")]
|
||||
[DataRow("My super Test String with \\EXC pattern.")]
|
||||
public void CustomFormatIgnoreInvalidPattern(string format)
|
||||
{
|
||||
// Act
|
||||
string result = TimeAndDateHelper.ConvertToCustomFormat(DateTime.Now, 0, 0, 1, "AD", format, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(format, result);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("DOW")]
|
||||
[DataRow("DIM")]
|
||||
[DataRow("WOM")]
|
||||
[DataRow("WOY")]
|
||||
[DataRow("EAB")]
|
||||
[DataRow("WFT")]
|
||||
[DataRow("UXT")]
|
||||
[DataRow("UMS")]
|
||||
[DataRow("OAD")]
|
||||
[DataRow("EXC")]
|
||||
[DataRow("EXF")]
|
||||
public void CustomFormatReplacesValidPattern(string format)
|
||||
{
|
||||
// Act
|
||||
string result = TimeAndDateHelper.ConvertToCustomFormat(DateTime.Now, 0, 0, 1, "AD", format, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
|
||||
|
||||
// Assert
|
||||
Assert.IsFalse(result.Contains(format, StringComparison.CurrentCulture));
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("01/01/0001", 1)] // First possible date
|
||||
[DataRow("12/31/9999", 5)] // Last possible date
|
||||
[DataRow("03/20/2025", 4)]
|
||||
[DataRow("09/01/2025", 1)] // First day in month is first day of week
|
||||
[DataRow("03/03/2025", 2)] // First monday is in second week
|
||||
public void GetWeekOfMonth(string date, int week)
|
||||
{
|
||||
// Act
|
||||
int result = TimeAndDateHelper.GetWeekOfMonth(DateTime.Parse(date, CultureInfo.GetCultureInfo("en-us")), DayOfWeek.Monday);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(result, week);
|
||||
}
|
||||
|
||||
[TestCleanup]
|
||||
public void CleanUp()
|
||||
{
|
||||
// Set culture to original value
|
||||
CultureInfo.CurrentCulture = originalCulture;
|
||||
CultureInfo.CurrentUICulture = originalUiCulture;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
{
|
||||
internal class AvailableResult
|
||||
internal sealed class AvailableResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the time/date value
|
||||
@@ -41,6 +41,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
ResultIconType.Time => $"Images\\time.{theme}.png",
|
||||
ResultIconType.Date => $"Images\\calendar.{theme}.png",
|
||||
ResultIconType.DateTime => $"Images\\timeDate.{theme}.png",
|
||||
ResultIconType.Error => $"Images\\Warning.{theme}.png",
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
@@ -51,5 +52,6 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
Time,
|
||||
Date,
|
||||
DateTime,
|
||||
Error,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.PowerToys.Run.Plugin.TimeDate.Properties;
|
||||
|
||||
namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
@@ -72,6 +72,86 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
string era = DateTimeFormatInfo.CurrentInfo.GetEraName(calendar.GetEra(dateTimeNow));
|
||||
string eraShort = DateTimeFormatInfo.CurrentInfo.GetAbbreviatedEraName(calendar.GetEra(dateTimeNow));
|
||||
|
||||
// Custom formats
|
||||
foreach (string f in TimeDateSettings.Instance.CustomFormats)
|
||||
{
|
||||
string[] formatParts = f.Split("=", 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
string formatSyntax = formatParts.Length == 2 ? formatParts[1] : string.Empty;
|
||||
string searchTags = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagCustom");
|
||||
DateTime dtObject = dateTimeNow;
|
||||
|
||||
// If Length = 0 then empty string.
|
||||
if (formatParts.Length >= 1)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Verify and check input and update search tags
|
||||
if (formatParts.Length == 1)
|
||||
{
|
||||
throw new FormatException("Format syntax part after equal sign is missing.");
|
||||
}
|
||||
|
||||
bool containsCustomSyntax = TimeAndDateHelper.StringContainsCustomFormatSyntax(formatSyntax);
|
||||
if (formatSyntax.StartsWith("UTC:", StringComparison.InvariantCulture))
|
||||
{
|
||||
searchTags = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagCustomUtc");
|
||||
dtObject = dateTimeNowUtc;
|
||||
}
|
||||
|
||||
// Get formated date
|
||||
var value = TimeAndDateHelper.ConvertToCustomFormat(dtObject, unixTimestamp, unixTimestampMilliseconds, weekOfYear, eraShort, Regex.Replace(formatSyntax, "^UTC:", string.Empty), firstWeekRule, firstDayOfTheWeek);
|
||||
try
|
||||
{
|
||||
value = dtObject.ToString(value, CultureInfo.CurrentCulture);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (!containsCustomSyntax)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do not fail as we have custom format syntax. Instead fix backslashes.
|
||||
value = Regex.Replace(value, @"(?<!\\)\\", string.Empty).Replace("\\\\", "\\");
|
||||
}
|
||||
}
|
||||
|
||||
// Add result
|
||||
results.Add(new AvailableResult()
|
||||
{
|
||||
Value = value,
|
||||
Label = formatParts[0],
|
||||
AlternativeSearchTag = searchTags,
|
||||
IconType = ResultIconType.DateTime,
|
||||
});
|
||||
}
|
||||
catch (ArgumentOutOfRangeException e)
|
||||
{
|
||||
Wox.Plugin.Logger.Log.Exception($"Failed to convert into custom format {formatParts[0]}: {formatSyntax}", e, typeof(AvailableResultsList));
|
||||
results.Add(new AvailableResult()
|
||||
{
|
||||
Value = Resources.Microsoft_plugin_timedate_ErrorConvertCustomFormat + " " + e.Message,
|
||||
Label = formatParts[0],
|
||||
AlternativeSearchTag = searchTags,
|
||||
IconType = ResultIconType.Error,
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Wox.Plugin.Logger.Log.Exception($"Failed to convert into custom format {formatParts[0]}: {formatSyntax}", e, typeof(AvailableResultsList));
|
||||
results.Add(new AvailableResult()
|
||||
{
|
||||
Value = Resources.Microsoft_plugin_timedate_InvalidCustomFormat + " " + formatSyntax,
|
||||
Label = formatParts[0],
|
||||
AlternativeSearchTag = searchTags,
|
||||
IconType = ResultIconType.Error,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Predefined formats
|
||||
results.AddRange(new[]
|
||||
{
|
||||
new AvailableResult()
|
||||
@@ -152,6 +232,13 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
IconType = ResultIconType.Date,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = DateTime.DaysInMonth(dateTimeNow.Year, dateTimeNow.Month).ToString(CultureInfo.CurrentCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_DaysInMonth,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
|
||||
IconType = ResultIconType.Date,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNow.DayOfYear.ToString(CultureInfo.CurrentCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_DayOfYear,
|
||||
@@ -201,6 +288,13 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
IconType = ResultIconType.Date,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = DateTime.IsLeapYear(dateTimeNow.Year) ? Resources.Microsoft_plugin_timedate_LeapYear : Resources.Microsoft_plugin_timedate_NoLeapYear,
|
||||
Label = Resources.Microsoft_plugin_timedate_LeapYear,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
|
||||
IconType = ResultIconType.Date,
|
||||
},
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = era,
|
||||
Label = Resources.Microsoft_plugin_timedate_Era,
|
||||
@@ -221,13 +315,31 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagDate"),
|
||||
IconType = ResultIconType.Date,
|
||||
},
|
||||
new AvailableResult()
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
results.Add(new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNow.ToFileTime().ToString(CultureInfo.CurrentCulture),
|
||||
Label = Resources.Microsoft_plugin_timedate_WindowsFileTime,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"),
|
||||
IconType = ResultIconType.DateTime,
|
||||
},
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
results.Add(new AvailableResult()
|
||||
{
|
||||
Value = Resources.Microsoft_plugin_timedate_ErrorConvertWft,
|
||||
Label = Resources.Microsoft_plugin_timedate_WindowsFileTime,
|
||||
AlternativeSearchTag = ResultHelper.SelectStringFromResources(isSystemDateTime, "Microsoft_plugin_timedate_SearchTagFormat"),
|
||||
IconType = ResultIconType.Error,
|
||||
});
|
||||
}
|
||||
|
||||
results.AddRange(new[]
|
||||
{
|
||||
new AvailableResult()
|
||||
{
|
||||
Value = dateTimeNowUtc.ToString("u"),
|
||||
|
||||
@@ -83,10 +83,10 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
/// Gets a result with an error message that only numbers can't be parsed
|
||||
/// </summary>
|
||||
/// <returns>Element of type <see cref="Result"/>.</returns>
|
||||
internal static Result CreateNumberErrorResult(string theme) => new Result()
|
||||
internal static Result CreateNumberErrorResult(string theme, string title, string subtitle) => new Result()
|
||||
{
|
||||
Title = Resources.Microsoft_plugin_timedate_ErrorResultTitle,
|
||||
SubTitle = Resources.Microsoft_plugin_timedate_ErrorResultSubTitle,
|
||||
Title = title,
|
||||
SubTitle = subtitle,
|
||||
ToolTipData = new ToolTipData(Resources.Microsoft_plugin_timedate_ErrorResultTitle, Resources.Microsoft_plugin_timedate_ErrorResultSubTitle),
|
||||
IcoPath = $"Images\\Warning.{theme}.png",
|
||||
};
|
||||
|
||||
@@ -40,9 +40,12 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
List<AvailableResult> availableFormats = new List<AvailableResult>();
|
||||
List<Result> results = new List<Result>();
|
||||
bool isKeywordSearch = !string.IsNullOrEmpty(query.ActionKeyword);
|
||||
bool isEmptySearchInput = string.IsNullOrEmpty(query.Search);
|
||||
bool isEmptySearchInput = string.IsNullOrWhiteSpace(query.Search);
|
||||
string searchTerm = query.Search;
|
||||
|
||||
// Last input parsing error
|
||||
string lastInputParsingErrorReason = string.Empty;
|
||||
|
||||
// Conjunction search without keyword => return no results
|
||||
// (This improves the results on global queries.)
|
||||
if (!isKeywordSearch && _conjunctionList.Any(x => x.Equals(searchTerm, StringComparison.CurrentCultureIgnoreCase)))
|
||||
@@ -61,13 +64,13 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
{
|
||||
// Search for specified format with specified time/date value
|
||||
var userInput = searchTerm.Split(InputDelimiter);
|
||||
if (TimeAndDateHelper.ParseStringAsDateTime(userInput[1], out DateTime timestamp))
|
||||
if (TimeAndDateHelper.ParseStringAsDateTime(userInput[1], out DateTime timestamp, out lastInputParsingErrorReason))
|
||||
{
|
||||
availableFormats.AddRange(AvailableResultsList.GetList(isKeywordSearch, null, null, timestamp));
|
||||
searchTerm = userInput[0];
|
||||
}
|
||||
}
|
||||
else if (TimeAndDateHelper.ParseStringAsDateTime(searchTerm, out DateTime timestamp))
|
||||
else if (TimeAndDateHelper.ParseStringAsDateTime(searchTerm, out DateTime timestamp, out lastInputParsingErrorReason))
|
||||
{
|
||||
// Return all formats for specified time/date value
|
||||
availableFormats.AddRange(AvailableResultsList.GetList(isKeywordSearch, null, null, timestamp));
|
||||
@@ -122,12 +125,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
}
|
||||
|
||||
// If search term is only a number that can't be parsed return an error message
|
||||
if (!isEmptySearchInput && results.Count == 0 && Regex.IsMatch(searchTerm, @"\w+\d+.*$") && !searchTerm.Any(char.IsWhiteSpace) && (TimeAndDateHelper.IsSpecialInputParsing(searchTerm) || !Regex.IsMatch(searchTerm, @"\d+[\.:/]\d+")))
|
||||
if (!isEmptySearchInput && results.Count == 0 && Regex.IsMatch(searchTerm, @"\w+[+-]?\d+.*$") && !searchTerm.Any(char.IsWhiteSpace) && (TimeAndDateHelper.IsSpecialInputParsing(searchTerm) || !Regex.IsMatch(searchTerm, @"\d+[\.:/]\d+")))
|
||||
{
|
||||
// Without plugin key word show only if message is not hidden by setting
|
||||
string title = !string.IsNullOrEmpty(lastInputParsingErrorReason) ? Resources.Microsoft_plugin_timedate_ErrorResultValue : Resources.Microsoft_plugin_timedate_ErrorResultTitle;
|
||||
string message = !string.IsNullOrEmpty(lastInputParsingErrorReason) ? lastInputParsingErrorReason : Resources.Microsoft_plugin_timedate_ErrorResultSubTitle;
|
||||
|
||||
// Without plugin key word show only if not hidden by setting
|
||||
if (isKeywordSearch || !TimeDateSettings.Instance.HideNumberMessageOnGlobalQuery)
|
||||
{
|
||||
results.Add(ResultHelper.CreateNumberErrorResult(iconTheme));
|
||||
results.Add(ResultHelper.CreateNumberErrorResult(iconTheme, title, message));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.PowerToys.Run.Plugin.TimeDate.Properties;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.PowerToys.Run.Plugin.TimeDate.UnitTests")]
|
||||
|
||||
@@ -13,6 +15,33 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
{
|
||||
internal static class TimeAndDateHelper
|
||||
{
|
||||
private static readonly Regex _regexSpecialInputFormats = new Regex(@"^.*(::)?(u|ums|ft|oa|exc|exf)[+-]\d");
|
||||
private static readonly Regex _regexCustomDateTimeFormats = new Regex(@"(?<!\\)(DOW|DIM|WOM|WOY|EAB|WFT|UXT|UMS|OAD|EXC|EXF)");
|
||||
private static readonly Regex _regexCustomDateTimeDow = new Regex(@"(?<!\\)DOW");
|
||||
private static readonly Regex _regexCustomDateTimeDim = new Regex(@"(?<!\\)DIM");
|
||||
private static readonly Regex _regexCustomDateTimeWom = new Regex(@"(?<!\\)WOM");
|
||||
private static readonly Regex _regexCustomDateTimeWoy = new Regex(@"(?<!\\)WOY");
|
||||
private static readonly Regex _regexCustomDateTimeEab = new Regex(@"(?<!\\)EAB");
|
||||
private static readonly Regex _regexCustomDateTimeWft = new Regex(@"(?<!\\)WFT");
|
||||
private static readonly Regex _regexCustomDateTimeUxt = new Regex(@"(?<!\\)UXT");
|
||||
private static readonly Regex _regexCustomDateTimeUms = new Regex(@"(?<!\\)UMS");
|
||||
private static readonly Regex _regexCustomDateTimeOad = new Regex(@"(?<!\\)OAD");
|
||||
private static readonly Regex _regexCustomDateTimeExc = new Regex(@"(?<!\\)EXC");
|
||||
private static readonly Regex _regexCustomDateTimeExf = new Regex(@"(?<!\\)EXF");
|
||||
|
||||
private const long UnixTimeSecondsMin = -62135596800;
|
||||
private const long UnixTimeSecondsMax = 253402300799;
|
||||
private const long UnixTimeMillisecondsMin = -62135596800000;
|
||||
private const long UnixTimeMillisecondsMax = 253402300799999;
|
||||
private const long WindowsFileTimeMin = 0;
|
||||
private const long WindowsFileTimeMax = 2650467707991000000;
|
||||
private const double OADateMin = -657434.99999999;
|
||||
private const double OADateMax = 2958465.99999999;
|
||||
private const double Excel1900DateMin = 1;
|
||||
private const double Excel1900DateMax = 2958465.99998843;
|
||||
private const double Excel1904DateMin = 0;
|
||||
private const double Excel1904DateMax = 2957003.99998843;
|
||||
|
||||
/// <summary>
|
||||
/// Get the format for the time string
|
||||
/// </summary>
|
||||
@@ -56,18 +85,25 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
/// Returns the number week in the month (Used code from 'David Morton' from <see href="https://social.msdn.microsoft.com/Forums/vstudio/bf504bba-85cb-492d-a8f7-4ccabdf882cb/get-week-number-for-month"/>)
|
||||
/// </summary>
|
||||
/// <param name="date">date</param>
|
||||
/// <param name="formatSettingFirstDayOfWeek">Setting for the first day in the week.</param>
|
||||
/// <returns>Number of week in the month</returns>
|
||||
internal static int GetWeekOfMonth(DateTime date, DayOfWeek formatSettingFirstDayOfWeek)
|
||||
{
|
||||
DateTime beginningOfMonth = new DateTime(date.Year, date.Month, 1);
|
||||
int adjustment = 1; // We count from 1 to 7 and not from 0 to 6
|
||||
int weekCount = 1;
|
||||
|
||||
while (date.Date.AddDays(1).DayOfWeek != formatSettingFirstDayOfWeek)
|
||||
for (int i = 1; i <= date.Day; i++)
|
||||
{
|
||||
date = date.AddDays(1);
|
||||
DateTime d = new(date.Year, date.Month, i);
|
||||
|
||||
// Count week number +1 if day is the first day of a week and not day 1 of the month.
|
||||
// (If we count on day one of a month we would start the month with week number 2.)
|
||||
if (i > 1 && d.DayOfWeek == formatSettingFirstDayOfWeek)
|
||||
{
|
||||
weekCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return (int)Math.Truncate((double)date.Subtract(beginningOfMonth).TotalDays / 7f) + adjustment;
|
||||
return weekCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -83,40 +119,170 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
return ((date.DayOfWeek + daysInWeek - formatSettingFirstDayOfWeek) % daysInWeek) + adjustment;
|
||||
}
|
||||
|
||||
internal static double ConvertToOleAutomationFormat(DateTime date, OADateFormats type)
|
||||
{
|
||||
double v = date.ToOADate();
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case OADateFormats.Excel1904:
|
||||
// Excel with base 1904: Adjust by -1462
|
||||
v -= 1462;
|
||||
|
||||
// Date starts at 1/1/1904 = 0
|
||||
if (Math.Truncate(v) < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Not a valid Excel date.", innerException: null);
|
||||
}
|
||||
|
||||
return v;
|
||||
case OADateFormats.Excel1900:
|
||||
// Excel with base 1900: Adjust by -1 if v < 61
|
||||
v = v < 61 ? v - 1 : v;
|
||||
|
||||
// Date starts at 1/1/1900 = 1
|
||||
if (Math.Truncate(v) < 1)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Not a valid Excel date.", innerException: null);
|
||||
}
|
||||
|
||||
return v;
|
||||
default:
|
||||
// OLE Automation date: Return as is.
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert input string to a <see cref="DateTime"/> object in local time
|
||||
/// </summary>
|
||||
/// <param name="input">String with date/time</param>
|
||||
/// <param name="timestamp">The new <see cref="DateTime"/> object</param>
|
||||
/// <param name="inputParsingErrorMsg">Error message shown to the user</param>
|
||||
/// <returns>True on success, otherwise false</returns>
|
||||
internal static bool ParseStringAsDateTime(in string input, out DateTime timestamp)
|
||||
internal static bool ParseStringAsDateTime(in string input, out DateTime timestamp, out string inputParsingErrorMsg)
|
||||
{
|
||||
inputParsingErrorMsg = string.Empty;
|
||||
CompositeFormat errorMessage = CompositeFormat.Parse(Resources.Microsoft_plugin_timedate_InvalidInput_SupportedRange);
|
||||
|
||||
if (DateTime.TryParse(input, out timestamp))
|
||||
{
|
||||
// Known date/time format
|
||||
return true;
|
||||
}
|
||||
else if (Regex.IsMatch(input, @"^u[\+-]?\d{1,10}$") && long.TryParse(input.TrimStart('u'), out long secondsU))
|
||||
else if (Regex.IsMatch(input, @"^u[\+-]?\d+$"))
|
||||
{
|
||||
// Unix time stamp
|
||||
// We use long instead of int, because int is too small after 03:14:07 UTC 2038-01-19
|
||||
var canParse = long.TryParse(input.TrimStart('u'), out var secondsU);
|
||||
|
||||
// Value has to be in the range from -62135596800 to 253402300799
|
||||
if (!canParse || secondsU < UnixTimeSecondsMin || secondsU > UnixTimeSecondsMax)
|
||||
{
|
||||
inputParsingErrorMsg = string.Format(CultureInfo.CurrentCulture, errorMessage, Resources.Microsoft_plugin_timedate_Unix, UnixTimeSecondsMin, UnixTimeSecondsMax);
|
||||
timestamp = new DateTime(1, 1, 1, 1, 1, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
timestamp = DateTimeOffset.FromUnixTimeSeconds(secondsU).LocalDateTime;
|
||||
return true;
|
||||
}
|
||||
else if (Regex.IsMatch(input, @"^ums[\+-]?\d{1,13}$") && long.TryParse(input.TrimStart("ums".ToCharArray()), out long millisecondsUms))
|
||||
else if (Regex.IsMatch(input, @"^ums[\+-]?\d+$"))
|
||||
{
|
||||
// Unix time stamp in milliseconds
|
||||
// We use long instead of int because int is too small after 03:14:07 UTC 2038-01-19
|
||||
var canParse = long.TryParse(input.TrimStart("ums".ToCharArray()), out var millisecondsUms);
|
||||
|
||||
// Value has to be in the range from -62135596800000 to 253402300799999
|
||||
if (!canParse || millisecondsUms < UnixTimeMillisecondsMin || millisecondsUms > UnixTimeMillisecondsMax)
|
||||
{
|
||||
inputParsingErrorMsg = string.Format(CultureInfo.CurrentCulture, errorMessage, Resources.Microsoft_plugin_timedate_Unix_Milliseconds, UnixTimeMillisecondsMin, UnixTimeMillisecondsMax);
|
||||
timestamp = new DateTime(1, 1, 1, 1, 1, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
timestamp = DateTimeOffset.FromUnixTimeMilliseconds(millisecondsUms).LocalDateTime;
|
||||
return true;
|
||||
}
|
||||
else if (Regex.IsMatch(input, @"^ft\d+$") && long.TryParse(input.TrimStart("ft".ToCharArray()), out long secondsFt))
|
||||
else if (Regex.IsMatch(input, @"^ft\d+$"))
|
||||
{
|
||||
var canParse = long.TryParse(input.TrimStart("ft".ToCharArray()), out var secondsFt);
|
||||
|
||||
// Windows file time
|
||||
// Value has to be in the range from 0 to 2650467707991000000
|
||||
if (!canParse || secondsFt < WindowsFileTimeMin || secondsFt > WindowsFileTimeMax)
|
||||
{
|
||||
inputParsingErrorMsg = string.Format(CultureInfo.CurrentCulture, errorMessage, Resources.Microsoft_plugin_timedate_WindowsFileTime, WindowsFileTimeMin, WindowsFileTimeMax);
|
||||
timestamp = new DateTime(1, 1, 1, 1, 1, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
// DateTime.FromFileTime returns as local time.
|
||||
timestamp = DateTime.FromFileTime(secondsFt);
|
||||
return true;
|
||||
}
|
||||
else if (Regex.IsMatch(input, @"^oa[+-]?\d+[,.0-9]*$"))
|
||||
{
|
||||
var canParse = double.TryParse(input.TrimStart("oa".ToCharArray()), out var oADate);
|
||||
|
||||
// OLE Automation date
|
||||
// Input has to be in the range from -657434.99999999 to 2958465.99999999
|
||||
// DateTime.FromOADate returns as local time.
|
||||
if (!canParse || oADate < OADateMin || oADate > OADateMax)
|
||||
{
|
||||
inputParsingErrorMsg = string.Format(CultureInfo.CurrentCulture, errorMessage, Resources.Microsoft_plugin_timedate_OADate, OADateMin, OADateMax);
|
||||
timestamp = new DateTime(1, 1, 1, 1, 1, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
timestamp = DateTime.FromOADate(oADate);
|
||||
return true;
|
||||
}
|
||||
else if (Regex.IsMatch(input, @"^exc[+-]?\d+[,.0-9]*$"))
|
||||
{
|
||||
var canParse = double.TryParse(input.TrimStart("exc".ToCharArray()), out var excDate);
|
||||
|
||||
// Excel's 1900 date value
|
||||
// Input has to be in the range from 1 (0 = Fake date) to 2958465.99998843 and not 60 whole number
|
||||
// Because of a bug in Excel and the way it behaves before 3/1/1900 we have to adjust all inputs lower than 61 for +1
|
||||
// DateTime.FromOADate returns as local time.
|
||||
if (!canParse || excDate < 0 || excDate > Excel1900DateMax)
|
||||
{
|
||||
// For the if itself we use 0 as min value that we can show a special message if input is 0.
|
||||
inputParsingErrorMsg = string.Format(CultureInfo.CurrentCulture, errorMessage, Resources.Microsoft_plugin_timedate_Excel1900, Excel1900DateMin, Excel1900DateMax);
|
||||
timestamp = new DateTime(1, 1, 1, 1, 1, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Math.Truncate(excDate) == 0 || Math.Truncate(excDate) == 60)
|
||||
{
|
||||
inputParsingErrorMsg = Resources.Microsoft_plugin_timedate_InvalidInput_FakeExcel1900;
|
||||
timestamp = new DateTime(1, 1, 1, 1, 1, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
excDate = excDate <= 60 ? excDate + 1 : excDate;
|
||||
timestamp = DateTime.FromOADate(excDate);
|
||||
return true;
|
||||
}
|
||||
else if (Regex.IsMatch(input, @"^exf[+-]?\d+[,.0-9]*$"))
|
||||
{
|
||||
var canParse = double.TryParse(input.TrimStart("exf".ToCharArray()), out var exfDate);
|
||||
|
||||
// Excel's 1904 date value
|
||||
// Input has to be in the range from 0 to 2957003.99998843
|
||||
// Because Excel uses 01/01/1904 as base we need to adjust for +1462
|
||||
// DateTime.FromOADate returns as local time.
|
||||
if (!canParse || exfDate < Excel1904DateMin || exfDate > Excel1904DateMax)
|
||||
{
|
||||
inputParsingErrorMsg = string.Format(CultureInfo.CurrentCulture, errorMessage, Resources.Microsoft_plugin_timedate_Excel1904, Excel1904DateMin, Excel1904DateMax);
|
||||
timestamp = new DateTime(1, 1, 1, 1, 1, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
timestamp = DateTime.FromOADate(exfDate + 1462);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
timestamp = new DateTime(1, 1, 1, 1, 1, 1);
|
||||
@@ -125,13 +291,85 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test if input is special parsing for Unix time, Unix time in milliseconds or File time.
|
||||
/// Test if input is special parsing for Unix time, Unix time in milliseconds, file time, ...
|
||||
/// </summary>
|
||||
/// <param name="input">String with date/time</param>
|
||||
/// <returns>True if yes, otherwise false</returns>
|
||||
internal static bool IsSpecialInputParsing(string input)
|
||||
{
|
||||
return Regex.IsMatch(input, @"^.*(u|ums|ft)\d");
|
||||
return _regexSpecialInputFormats.IsMatch(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a DateTime object based on the format string
|
||||
/// </summary>
|
||||
/// <param name="date">Date/time object.</param>
|
||||
/// <param name="unix">Value for replacing "Unix Time Stamp".</param>
|
||||
/// <param name="unixMilliseconds">Value for replacing "Unix Time Stamp in milliseconds".</param>
|
||||
/// <param name="calWeek">Value for relacing calendar week.</param>
|
||||
/// <param name="eraShortFormat">Era abbreviation.</param>
|
||||
/// <param name="format">Format definition.</param>
|
||||
/// <returns>Formated date/time string.</returns>
|
||||
internal static string ConvertToCustomFormat(DateTime date, long unix, long unixMilliseconds, int calWeek, string eraShortFormat, string format, CalendarWeekRule firstWeekRule, DayOfWeek firstDayOfTheWeek)
|
||||
{
|
||||
string result = format;
|
||||
|
||||
// DOW: Number of day in week
|
||||
result = _regexCustomDateTimeDow.Replace(result, GetNumberOfDayInWeek(date, firstDayOfTheWeek).ToString(CultureInfo.CurrentCulture));
|
||||
|
||||
// DIM: Days in Month
|
||||
result = _regexCustomDateTimeDim.Replace(result, DateTime.DaysInMonth(date.Year, date.Month).ToString(CultureInfo.CurrentCulture));
|
||||
|
||||
// WOM: Week of Month
|
||||
result = _regexCustomDateTimeWom.Replace(result, GetWeekOfMonth(date, firstDayOfTheWeek).ToString(CultureInfo.CurrentCulture));
|
||||
|
||||
// WOY: Week of Year
|
||||
result = _regexCustomDateTimeWoy.Replace(result, calWeek.ToString(CultureInfo.CurrentCulture));
|
||||
|
||||
// EAB: Era abbreviation
|
||||
result = _regexCustomDateTimeEab.Replace(result, eraShortFormat);
|
||||
|
||||
// WFT: Week of Month
|
||||
if (_regexCustomDateTimeWft.IsMatch(result))
|
||||
{
|
||||
// Special handling as very early dates can't convert.
|
||||
result = _regexCustomDateTimeWft.Replace(result, date.ToFileTime().ToString(CultureInfo.CurrentCulture));
|
||||
}
|
||||
|
||||
// UXT: Unix time stamp
|
||||
result = _regexCustomDateTimeUxt.Replace(result, unix.ToString(CultureInfo.CurrentCulture));
|
||||
|
||||
// UMS: Unix time stamp milli seconds
|
||||
result = _regexCustomDateTimeUms.Replace(result, unixMilliseconds.ToString(CultureInfo.CurrentCulture));
|
||||
|
||||
// OAD: OLE Automation date
|
||||
result = _regexCustomDateTimeOad.Replace(result, ConvertToOleAutomationFormat(date, OADateFormats.OLEAutomation).ToString(CultureInfo.CurrentCulture));
|
||||
|
||||
// EXC: Excel date value with base 1900
|
||||
if (_regexCustomDateTimeExc.IsMatch(result))
|
||||
{
|
||||
// Special handling as very early dates can't convert.
|
||||
result = _regexCustomDateTimeExc.Replace(result, ConvertToOleAutomationFormat(date, OADateFormats.Excel1900).ToString(CultureInfo.CurrentCulture));
|
||||
}
|
||||
|
||||
// EXF: Excel date value with base 1904
|
||||
if (_regexCustomDateTimeExf.IsMatch(result))
|
||||
{
|
||||
// Special handling as very early dates can't convert.
|
||||
result = _regexCustomDateTimeExf.Replace(result, ConvertToOleAutomationFormat(date, OADateFormats.Excel1904).ToString(CultureInfo.CurrentCulture));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test a string for our custom date and time format syntax
|
||||
/// </summary>
|
||||
/// <param name="str">String to test.</param>
|
||||
/// <returns>True if yes and otherwise false</returns>
|
||||
internal static bool StringContainsCustomFormatSyntax(string str)
|
||||
{
|
||||
return _regexCustomDateTimeFormats.IsMatch(str);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -190,4 +428,14 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
Date,
|
||||
DateTime,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Different versions of Date formats based on OLE Automation date
|
||||
/// </summary>
|
||||
internal enum OADateFormats
|
||||
{
|
||||
OLEAutomation,
|
||||
Excel1900,
|
||||
Excel1904,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,11 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
/// </summary>
|
||||
internal bool HideNumberMessageOnGlobalQuery { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value containing the custom format definitions
|
||||
/// </summary>
|
||||
internal List<string> CustomFormats { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TimeDateSettings"/> class.
|
||||
/// Private constructor to make sure there is never more than one instance of this class
|
||||
@@ -100,29 +105,6 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
{
|
||||
var optionList = new List<PluginAdditionalOption>
|
||||
{
|
||||
new PluginAdditionalOption()
|
||||
{
|
||||
Key = nameof(CalendarFirstWeekRule),
|
||||
DisplayLabel = Resources.Microsoft_plugin_timedate_SettingFirstWeekRule,
|
||||
DisplayDescription = Resources.Microsoft_plugin_timedate_SettingFirstWeekRule_Description,
|
||||
PluginOptionType = PluginAdditionalOption.AdditionalOptionType.Combobox,
|
||||
ComboBoxItems = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_Setting_UseSystemSetting, "-1"),
|
||||
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_SettingFirstWeekRule_FirstDay, "0"),
|
||||
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_SettingFirstWeekRule_FirstFullWeek, "1"),
|
||||
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_SettingFirstWeekRule_FirstFourDayWeek, "2"),
|
||||
},
|
||||
ComboBoxValue = -1,
|
||||
},
|
||||
new PluginAdditionalOption()
|
||||
{
|
||||
Key = nameof(FirstDayOfWeek),
|
||||
DisplayLabel = Resources.Microsoft_plugin_timedate_SettingFirstDayOfWeek,
|
||||
PluginOptionType = PluginAdditionalOption.AdditionalOptionType.Combobox,
|
||||
ComboBoxItems = GetSortedListForWeekDaySetting(),
|
||||
ComboBoxValue = -1,
|
||||
},
|
||||
new PluginAdditionalOption()
|
||||
{
|
||||
Key = nameof(OnlyDateTimeNowGlobal),
|
||||
@@ -150,6 +132,38 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
DisplayLabel = Resources.Microsoft_plugin_timedate_SettingHideNumberMessageOnGlobalQuery,
|
||||
Value = false,
|
||||
},
|
||||
new PluginAdditionalOption()
|
||||
{
|
||||
Key = nameof(CalendarFirstWeekRule),
|
||||
DisplayLabel = Resources.Microsoft_plugin_timedate_SettingFirstWeekRule,
|
||||
DisplayDescription = Resources.Microsoft_plugin_timedate_SettingFirstWeekRule_Description,
|
||||
PluginOptionType = PluginAdditionalOption.AdditionalOptionType.Combobox,
|
||||
ComboBoxItems = new List<KeyValuePair<string, string>>
|
||||
{
|
||||
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_Setting_UseSystemSetting, "-1"),
|
||||
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_SettingFirstWeekRule_FirstDay, "0"),
|
||||
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_SettingFirstWeekRule_FirstFullWeek, "1"),
|
||||
new KeyValuePair<string, string>(Resources.Microsoft_plugin_timedate_SettingFirstWeekRule_FirstFourDayWeek, "2"),
|
||||
},
|
||||
ComboBoxValue = -1,
|
||||
},
|
||||
new PluginAdditionalOption()
|
||||
{
|
||||
Key = nameof(FirstDayOfWeek),
|
||||
DisplayLabel = Resources.Microsoft_plugin_timedate_SettingFirstDayOfWeek,
|
||||
PluginOptionType = PluginAdditionalOption.AdditionalOptionType.Combobox,
|
||||
ComboBoxItems = GetSortedListForWeekDaySetting(),
|
||||
ComboBoxValue = -1,
|
||||
},
|
||||
new PluginAdditionalOption()
|
||||
{
|
||||
Key = nameof(CustomFormats),
|
||||
PluginOptionType = PluginAdditionalOption.AdditionalOptionType.MultilineTextbox,
|
||||
DisplayLabel = Resources.Microsoft_plugin_timedate_Setting_CustomFormats,
|
||||
DisplayDescription = string.Format(CultureInfo.CurrentCulture, Resources.Microsoft_plugin_timedate_Setting_CustomFormatsDescription.ToString(), "DOW", "DIM", "WOM", "WOY", "EAB", "WFT", "UXT", "UMS", "OAD", "EXC", "EXF", "UTC:"),
|
||||
PlaceholderText = "MyFormat=dd-MMM-yyyy\rMySecondFormat=dddd (Da\\y nu\\mber: DOW)\rMyUtcFormat=UTC:hh:mm:ss",
|
||||
TextValue = string.Empty,
|
||||
},
|
||||
};
|
||||
|
||||
return optionList;
|
||||
@@ -172,6 +186,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
TimeWithSeconds = GetSettingOrDefault(settings, nameof(TimeWithSeconds));
|
||||
DateWithWeekday = GetSettingOrDefault(settings, nameof(DateWithWeekday));
|
||||
HideNumberMessageOnGlobalQuery = GetSettingOrDefault(settings, nameof(HideNumberMessageOnGlobalQuery));
|
||||
CustomFormats = GetMultilineTextSettingOrDefault(settings, nameof(CustomFormats));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -204,6 +219,21 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components
|
||||
return option?.ComboBoxValue ?? GetAdditionalOptions().First(x => x.Key == name).ComboBoxValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the combobox value of the given settings list with the given name.
|
||||
/// </summary>
|
||||
/// <param name="settings">The object that contain all settings.</param>
|
||||
/// <param name="name">The name of the setting.</param>
|
||||
/// <returns>A settings value.</returns>
|
||||
private static List<string> GetMultilineTextSettingOrDefault(PowerLauncherPluginSettings settings, string name)
|
||||
{
|
||||
var option = settings?.AdditionalOptions?.FirstOrDefault(x => x.Key == name);
|
||||
|
||||
// If a setting isn't available, we use the value defined in the method GetAdditionalOptions() as fallback.
|
||||
// We can use First() instead of FirstOrDefault() because the values must exist. Otherwise, we made a mistake when defining the settings.
|
||||
return option?.TextValueAsMultilineList ?? GetAdditionalOptions().First(x => x.Key == name).TextValueAsMultilineList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a sorted list of values for the combo box of 'first day of week' setting.
|
||||
/// The list is sorted based on the current system culture setting.
|
||||
|
||||
@@ -150,6 +150,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Days in month.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_DaysInMonth {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_DaysInMonth", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Era.
|
||||
/// </summary>
|
||||
@@ -169,7 +178,25 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Valid prefixes: 'u' for Unix Timestamp, 'ums' for Unix Timestamp in milliseconds, 'ft' for Windows file time.
|
||||
/// Looks up a localized string similar to Failed to convert into custom format:.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_ErrorConvertCustomFormat {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_ErrorConvertCustomFormat", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Not a valid Windows file time.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_ErrorConvertWft {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_ErrorConvertWft", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Valid prefixes: 'u' for Unix Timestamp, 'ums' for Unix Timestamp in milliseconds, 'ft' for Windows file time, 'oa' for OLE Automation date, 'exc' for Excel's 1900 date value, 'exf' for Excel's 1904 date value.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_ErrorResultSubTitle {
|
||||
get {
|
||||
@@ -178,7 +205,7 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Error: Invalid number input.
|
||||
/// Looks up a localized string similar to Error: Invalid input.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_ErrorResultTitle {
|
||||
get {
|
||||
@@ -186,6 +213,33 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Error: Invalid number.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_ErrorResultValue {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_ErrorResultValue", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Excel's 1900 date value.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_Excel1900 {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_Excel1900", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Excel's 1904 date value.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_Excel1904 {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_Excel1904", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Date and time in filename-compatible format.
|
||||
/// </summary>
|
||||
@@ -204,6 +258,33 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid custom format:.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_InvalidCustomFormat {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_InvalidCustomFormat", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cannot parse the input as Excel's 1900 date value because it is a fake date. (In Excel 0 stands for 0/1/1900 and this date doesn't exist. And 60 stands for 2/29/1900 and this date only exists in Excel for compatibility with Lotus 123.).
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_InvalidInput_FakeExcel1900 {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_InvalidInput_FakeExcel1900", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Your input for {0} is outside the range from {1} to {2}..
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_InvalidInput_SupportedRange {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_InvalidInput_SupportedRange", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to ISO 8601.
|
||||
/// </summary>
|
||||
@@ -240,6 +321,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Leap year.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_LeapYear {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_LeapYear", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Millisecond.
|
||||
/// </summary>
|
||||
@@ -285,6 +375,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Not a leap year.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_NoLeapYear {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_NoLeapYear", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Now.
|
||||
/// </summary>
|
||||
@@ -303,6 +402,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to OLE Automation Date.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_OADate {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_OADate", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Provides time and date values for the system time or a custom time stamp (e.g.'{0}', '{1}', '{2}', '{3}').
|
||||
/// </summary>
|
||||
@@ -366,6 +474,42 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Date and time; Time and Date; Custom format.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_SearchTagCustom {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagCustom", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Current date and time; Current time and date; Now; Custom format.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_SearchTagCustomNow {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagCustomNow", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Date and time UTC; Time UTC and Date; Custom UTC format.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_SearchTagCustomUtc {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagCustomUtc", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Current date and time UTC; Current time UTC and date; Now UTC; Custom UTC format.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_SearchTagCustomUtcNow {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_SearchTagCustomUtcNow", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Date.
|
||||
/// </summary>
|
||||
@@ -447,6 +591,24 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Custom formats.
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_Setting_CustomFormats {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_Setting_CustomFormats", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Use date and time string format syntax and {0} (Day of Week), {1} (Days in Month), {2} (Week of Month), {3} (Week of the year), {4} (Era abbreviation), {5} (Windows File Time), {6} (Unix Time), {7} (Unix Time in milliseconds), {8} (OLE Automation date), {9} (Excel's 1900 based date value), {10} (Excel's 1904 based date value). If the format starts with {11}, then Universal Time (UTC) is used. (Use a backslash to escape format sequences and the backslash character as text.).
|
||||
/// </summary>
|
||||
internal static string Microsoft_plugin_timedate_Setting_CustomFormatsDescription {
|
||||
get {
|
||||
return ResourceManager.GetString("Microsoft_plugin_timedate_Setting_CustomFormatsDescription", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Use system setting.
|
||||
/// </summary>
|
||||
|
||||
@@ -156,10 +156,13 @@
|
||||
<value>Era abbreviation</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_ErrorResultSubTitle" xml:space="preserve">
|
||||
<value>Valid prefixes: 'u' for Unix Timestamp, 'ums' for Unix Timestamp in milliseconds, 'ft' for Windows file time</value>
|
||||
<value>Valid prefixes: 'u' for Unix Timestamp, 'ums' for Unix Timestamp in milliseconds, 'ft' for Windows file time, 'oa' for OLE Automation date, 'exc' for Excel's 1900 date value, 'exf' for Excel's 1904 date value</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_ErrorResultTitle" xml:space="preserve">
|
||||
<value>Error: Invalid number input</value>
|
||||
<value>Error: Invalid input</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_ErrorResultValue" xml:space="preserve">
|
||||
<value>Error: Invalid number</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_Hour" xml:space="preserve">
|
||||
<value>Hour</value>
|
||||
@@ -243,10 +246,26 @@
|
||||
<value>Date and time; Time and Date</value>
|
||||
<comment>Don't change order</comment>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_SearchTagCustom" xml:space="preserve">
|
||||
<value>Date and time; Time and Date; Custom format</value>
|
||||
<comment>Don't change order</comment>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_SearchTagCustomUtc" xml:space="preserve">
|
||||
<value>Date and time UTC; Time UTC and Date; Custom UTC format</value>
|
||||
<comment>Don't change order</comment>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_SearchTagFormatNow" xml:space="preserve">
|
||||
<value>Current date and time; Current time and date; Now</value>
|
||||
<comment>Don't change order</comment>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_SearchTagCustomNow" xml:space="preserve">
|
||||
<value>Current date and time; Current time and date; Now; Custom format</value>
|
||||
<comment>Don't change order</comment>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_SearchTagCustomUtcNow" xml:space="preserve">
|
||||
<value>Current date and time UTC; Current time UTC and date; Now UTC; Custom UTC format</value>
|
||||
<comment>Don't change order</comment>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_SearchTagTime" xml:space="preserve">
|
||||
<value>Time</value>
|
||||
<comment>Don't change order</comment>
|
||||
@@ -262,6 +281,9 @@
|
||||
<data name="Microsoft_plugin_timedate_Second" xml:space="preserve">
|
||||
<value>Second</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_InvalidCustomFormat" xml:space="preserve">
|
||||
<value>Invalid custom format:</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_SettingDateWithWeekday" xml:space="preserve">
|
||||
<value>Show date with weekday and name of month</value>
|
||||
</data>
|
||||
@@ -360,4 +382,42 @@
|
||||
<data name="Microsoft_plugin_timedate_Setting_UseSystemSetting" xml:space="preserve">
|
||||
<value>Use system setting</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_Setting_CustomFormats" xml:space="preserve">
|
||||
<value>Custom formats</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_Setting_CustomFormatsDescription" xml:space="preserve">
|
||||
<value>Use date and time string format syntax and {0} (Day of Week), {1} (Days in Month), {2} (Week of Month), {3} (Week of the year), {4} (Era abbreviation), {5} (Windows File Time), {6} (Unix Time), {7} (Unix Time in milliseconds), {8} (OLE Automation date), {9} (Excel's 1900 based date value), {10} (Excel's 1904 based date value). If the format starts with {11}, then Universal Time (UTC) is used. (Use a backslash to escape format sequences and the backslash character as text.)</value>
|
||||
<comment>The {n} parts are place holders and get replaced in the code.</comment>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_ErrorConvertCustomFormat" xml:space="preserve">
|
||||
<value>Failed to convert into custom format:</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_ErrorConvertWft" xml:space="preserve">
|
||||
<value>Not a valid Windows file time</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_InvalidInput_SupportedRange" xml:space="preserve">
|
||||
<value>Your input for {0} is outside the range from {1} to {2}.</value>
|
||||
<comment>The placeholder will be replace in code.</comment>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_OADate" xml:space="preserve">
|
||||
<value>OLE Automation Date</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_Excel1900" xml:space="preserve">
|
||||
<value>Excel's 1900 date value</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_InvalidInput_FakeExcel1900" xml:space="preserve">
|
||||
<value>Cannot parse the input as Excel's 1900 date value because it is a fake date. (In Excel 0 stands for 0/1/1900 and this date doesn't exist. And 60 stands for 2/29/1900 and this date only exists in Excel for compatibility with Lotus 123.)</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_Excel1904" xml:space="preserve">
|
||||
<value>Excel's 1904 date value</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_LeapYear" xml:space="preserve">
|
||||
<value>Leap year</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_NoLeapYear" xml:space="preserve">
|
||||
<value>Not a leap year</value>
|
||||
</data>
|
||||
<data name="Microsoft_plugin_timedate_DaysInMonth" xml:space="preserve">
|
||||
<value>Days in month</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -295,7 +295,7 @@
|
||||
"Areas": [ "AreaEaseOfAccess" ],
|
||||
"Type": "AppSettingsApp",
|
||||
"AltNames": [ "TouchFeedback" ],
|
||||
"Command": "ms-settings:easeofaccess-MousePointer"
|
||||
"Command": "ms-settings:easeofaccess-mousepointer"
|
||||
},
|
||||
{
|
||||
"Name": "Display",
|
||||
|
||||
@@ -28,18 +28,15 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
var bootTime = new System.Diagnostics.Stopwatch();
|
||||
bootTime.Start();
|
||||
|
||||
this.Activated += Window_Activated_SetIcon;
|
||||
|
||||
App.ThemeService.ThemeChanged += OnThemeChanged;
|
||||
App.ThemeService.ApplyTheme();
|
||||
|
||||
ShellPage.SetElevationStatus(App.IsElevated);
|
||||
ShellPage.SetIsUserAnAdmin(App.IsUserAnAdmin);
|
||||
|
||||
// Set window icon
|
||||
var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
|
||||
WindowId windowId = Win32Interop.GetWindowIdFromWindow(hWnd);
|
||||
AppWindow appWindow = AppWindow.GetFromWindowId(windowId);
|
||||
appWindow.SetIcon("Assets\\Settings\\icon.ico");
|
||||
|
||||
var placement = WindowHelper.DeserializePlacementOrDefault(hWnd);
|
||||
if (createHidden)
|
||||
{
|
||||
@@ -221,6 +218,15 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
App.ThemeService.ThemeChanged -= OnThemeChanged;
|
||||
}
|
||||
|
||||
private void Window_Activated_SetIcon(object sender, WindowActivatedEventArgs args)
|
||||
{
|
||||
// Set window icon
|
||||
var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
|
||||
WindowId windowId = Win32Interop.GetWindowIdFromWindow(hWnd);
|
||||
AppWindow appWindow = AppWindow.GetFromWindowId(windowId);
|
||||
appWindow.SetIcon("Assets\\Settings\\icon.ico");
|
||||
}
|
||||
|
||||
private void Window_Activated(object sender, WindowActivatedEventArgs args)
|
||||
{
|
||||
if (args.WindowActivationState != WindowActivationState.Deactivated)
|
||||
|
||||
@@ -31,7 +31,6 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
private WindowId _windowId;
|
||||
private IntPtr _hWnd;
|
||||
private AppWindow _appWindow;
|
||||
private WindowMessageMonitor _msgMonitor;
|
||||
private bool disposedValue;
|
||||
|
||||
public OobeWindow(PowerToysModules initialModule)
|
||||
@@ -41,15 +40,10 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
|
||||
this.InitializeComponent();
|
||||
|
||||
// Set window icon
|
||||
_hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
|
||||
_windowId = Win32Interop.GetWindowIdFromWindow(_hWnd);
|
||||
_appWindow = AppWindow.GetFromWindowId(_windowId);
|
||||
_appWindow.SetIcon("Assets\\Settings\\icon.ico");
|
||||
|
||||
OverlappedPresenter presenter = _appWindow.Presenter as OverlappedPresenter;
|
||||
presenter.IsMinimizable = false;
|
||||
presenter.IsMaximizable = false;
|
||||
this.Activated += Window_Activated_SetIcon;
|
||||
|
||||
var dpi = NativeMethods.GetDpiForWindow(_hWnd);
|
||||
_currentDPI = dpi;
|
||||
@@ -64,18 +58,6 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
|
||||
this.initialModule = initialModule;
|
||||
|
||||
_msgMonitor = new WindowMessageMonitor(this);
|
||||
_msgMonitor.WindowMessageReceived += (_, e) =>
|
||||
{
|
||||
const int WM_NCLBUTTONDBLCLK = 0x00A3;
|
||||
if (e.Message.MessageId == WM_NCLBUTTONDBLCLK)
|
||||
{
|
||||
// Disable double click on title bar to maximize window
|
||||
e.Result = 0;
|
||||
e.Handled = true;
|
||||
}
|
||||
};
|
||||
|
||||
this.SizeChanged += OobeWindow_SizeChanged;
|
||||
|
||||
var loader = Helpers.ResourceLoaderInstance.ResourceLoader;
|
||||
@@ -110,6 +92,12 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
}
|
||||
}
|
||||
|
||||
private void Window_Activated_SetIcon(object sender, WindowActivatedEventArgs args)
|
||||
{
|
||||
// Set window icon
|
||||
_appWindow.SetIcon("Assets\\Settings\\icon.ico");
|
||||
}
|
||||
|
||||
private void OobeWindow_SizeChanged(object sender, WindowSizeChangedEventArgs args)
|
||||
{
|
||||
var dpi = NativeMethods.GetDpiForWindow(_hWnd);
|
||||
@@ -149,8 +137,6 @@ namespace Microsoft.PowerToys.Settings.UI
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
_msgMonitor?.Dispose();
|
||||
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user