Compare commits

...

11 Commits

Author SHA1 Message Date
Leilei Zhang
6a0abecba2 add runner 2025-07-14 11:53:10 +08:00
Davide Giacometti
227c5d8147 [Peek] Terminate Preview Handlers Processes (#40116)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

This is an attempt to release Preview Handlers instantiated by Peek and
close the related processe.

⚠️ Note that even if the PR improve the current behavior, the solution
doesn't work 100% of times.
I noticed that sometimes the process gets leaked also when Preview
Handler is used in Explorer 🤔

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

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

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

- Ported the same fix applied to CmdPal to ensure the process is
terminated gracefully instead of being killed:
https://github.com/microsoft/PowerToys/pull/39589
- Attempt to cleanup Preview Handlers and close the relative process

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

Tested manually:
- Preview through some Excel and Word files
- Close Peek window
- Excel and Word processes are closed
2025-07-14 08:50:45 +08:00
Jiří Polášek
d258dcd61b CmdPal: Remove backdrop targets from existing backdrop controller before assigning a new one (#40540)
## Summary of the Pull Request
Also runs the code through UI thread. it will ensure correct access to
_acrylicController, and also because the method touches UI element.


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

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

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

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-07-12 14:38:01 -05:00
Jiří Polášek
1d464cc307 Use Empty content for empty Web search page (#40549)
## Summary of the Pull Request
Display full page message when the Web Search extension page is empty

<img width="786" height="473" alt="image"
src="https://github.com/user-attachments/assets/2d08d809-1127-44b3-9842-50969eb6bef7"
/>
<img width="786" height="473" alt="image"
src="https://github.com/user-attachments/assets/155374cc-3e13-4cc0-b7e5-b4fa2b371ba7"
/>

## PR Checklist

- [x] **Closes:** #38969 
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** yay
- [x] **Localization:** nope
- [x] **Dev docs:** none
- [x] **New binaries:** zilch
- [x] **Documentation updated:** no need

## Detailed Description of the Pull Request / Additional comments

--

## Validation Steps Performed

Tested with and without enabled history.
2025-07-11 21:11:13 -05:00
ruslanlap
3686d6ac19 Add PowerToysRun-Hotkeys plugin to third-party plugins documentation (#40530)
This PR adds the Hotkeys and RandomGen plugins to the third-party
plugins documentation in the General plugins section.

## Hotkeys Plugin
⌨️ Hotkeys for PowerToys Run - Create, manage, and trigger custom
keyboard shortcuts directly from PowerToys Run

![Hotkeys
Demo](https://github.com/ruslanlap/PowerToysRun-Hotkeys/blob/main/assets/demo-hotkeys.gif)

This plugin enables users to create and manage custom keyboard shortcuts
without leaving PowerToys Run, making it easier to automate repetitive
tasks and improve workflow efficiency.

### Features
- 🔑 Create custom keyboard shortcuts
- 🔄 Manage existing shortcuts
-  Trigger shortcuts directly from PowerToys Run
- 📋 Import/export shortcuts
- 🔍 Search through your shortcuts
- ⚙️ Configure shortcut behavior
- 🌐 Application-specific shortcuts
- 📝 Add descriptions to shortcuts

## RandomGen Plugin
🎲 RandomGen for PowerToys Run - Generate random data instantly with a
single keystroke

![RandomGen
Demo](https://github.com/ruslanlap/PowerToysRun-RandomGen/blob/master/assets/demo-randomgen-password.gif)

This plugin is particularly useful for designers who need random color
codes and placeholder content, as well as developers and testers who
need quick access to various types of random data.

### Features
- 🔐 Cryptographically Secure Passwords
- 📍 Secure PIN Codes
- 👤 Personal Data (names, emails, phone numbers)
- 🏢 Business Data
- 📅 Date Generation
- 🔢 Number Generation
- 🆔 Unique Identifiers (GUIDs/UUIDs)
- 🎨 Color Codes (especially useful for designers)
- 🌐 Web Data
- 💳 Payment Testing Data
- 📝 Lorem Ipsum placeholder text

## Links to plugins
- https://github.com/ruslanlap/PowerToysRun-Hotkeys
- https://github.com/ruslanlap/PowerToysRun-RandomGen
2025-07-11 12:18:17 -05:00
Jiří Polášek
a0495736f1 CmdPal: Hide Open URL fallback item when search query is empty or doesn't contain valid URL (#40514)
## Summary of the Pull Request
Clears and hides fallback item for URL when query changes and URL is no
longer a valid URL. Fixes the situation when the item remains visible in
the list with generic text and pointing the last valid URL it was
updated with.

## PR Checklist

- [x] **Closes:** #40512
- [ ] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [x] **Tests:** Added/updated and all pass
- [x] **Localization:** no new strings
- [x] **Dev docs:** nothing to update
- [x] **New binaries:** none
- [x] **Documentation updated:** none

## Detailed Description of the Pull Request / Additional comments

## Validation Steps Performed
Entered URL to search field. Observed the Open URL item is present.
Cleared URL. Observed that Open URL item is not present.
2025-07-11 12:08:31 -05:00
octastylos-pseudodipteros
22f565ca49 [QuickAccent] Add Vietnamese (#40164)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Adds support for Vietnamese in QuickAccent
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist

- [x] **Closes:** #36491
- [x] **Communication:** I've discussed this with core contributors
already. If the work hasn't been agreed, this work might be rejected
- [ ] **Tests:** Added/updated and all pass
- [x] **Localization:** All end-user-facing strings can be localized
- [x] **Dev docs:** no need
- [x] **New binaries:** no need
- [x] **Documentation updated:** no need

<!-- Provide a more detailed description of the PR, other things fixed,
or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments
Vietnamese character set based of on
[Wikipedia](https://en.wikipedia.org/wiki/Vietnamese_alphabet)
<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
Manually tested
2025-07-11 11:08:42 -05:00
Jiří Polášek
e041556395 CmdPal: Handle Alt+Left Arrow even when search bar contains text (#40516)
Previously, the `KeyDown` handler for `Alt+Left Arrow` was not invoked
if the search bar contained text. By moving the logic to the
`PreviewKeyDown` handler, we ensure the shortcut is handled regardless
of the search bar’s content.

The handler was added to the `ShellPage`'s `PreviewKeyDown` handler,
rather than the one on `SearchBar`. This will allow the shortcut to be
available throughout the entire window and will ensure it works from
within the page content, e.g. from forms

<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

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

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

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

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-07-11 11:08:17 -05:00
Mike Griese
b55c4eeed3 CmdPal: Remove ShellViewModel's dependency on MainListPage (#40482)
targets #40479

Kinda mental that the viewmodel had this hard dependency.

So instead I added a service for providing the root page for the app.
Theoretically, you could add a different `IRootPageService`, and give
out
a different root page.


ref #40113
2025-07-11 06:29:39 -05:00
leileizhang
9a65c36859 Fix WiX component GUID conflict between BgcodePreviewHandler and GcodePreviewHandler (#40553)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
Fixes WiX installer build failure caused by GUID conflict between
`BgcodePreviewHandler` and `GcodePreviewHandler` components in the
Resources.wxs file.

### Root cause
Both `BgcodePreviewHandler` and `GcodePreviewHandler` components were
using the same GUID suffix `07`, causing WiX build errors due to
duplicate component GUIDs across all language variations.

### Fix
Changed `BgcodePreviewHandler` component GUID suffix from `07` to `22`
to ensure unique component identification.

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

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

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

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-07-11 14:43:56 +08:00
leileizhang
668f3f3ebd [pipeline] Reduce CI, Fuzz, and UI Test Timeout from 4 Hours to 90 Minutes (#40500)
<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request

The previous timeout for CI, fuzzing, and UI test builds was set to 4
hours, which often caused long wait times even when a job was already
stuck or failing.

To improve development feedback loops and reduce wasted time, the
timeout has been shortened to 90 minutes, which should still be
sufficient for normal runs but helps catch issues earlier.

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

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

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

<!-- Describe how you validated the behavior. Add automated tests
wherever possible, but list manual validation steps taken as well -->
## Validation Steps Performed
2025-07-11 11:23:58 +08:00
24 changed files with 263 additions and 94 deletions

View File

@@ -43,6 +43,7 @@ stages:
enableMsBuildCaching: ${{ parameters.enableMsBuildCaching }}
runTests: true
useVSPreview: ${{ parameters.useVSPreview }}
timeoutInMinutes: 90
- stage: OneFuzz
displayName: Fuzz ${{ parameters.platform }}

View File

@@ -81,6 +81,12 @@ parameters:
- 'src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj'
- 'src/modules/previewpane/SvgThumbnailProvider/SvgThumbnailProvider.csproj'
- 'src/modules/FileLocksmith/FileLocksmithUI/FileLocksmithUI.csproj'
- name: timeoutInMinutes
type: number
default: 240
- name: cancelTimeoutInMinutes
type: number
default: 1
jobs:
- job: ${{ parameters.jobName }}
@@ -123,8 +129,8 @@ jobs:
${{ else }}:
RestoreAdditionalProjectSourcesArg: ''
displayName: Build
timeoutInMinutes: 240
cancelTimeoutInMinutes: 1
timeoutInMinutes: ${{ parameters.timeoutInMinutes }}
cancelTimeoutInMinutes: ${{ parameters.cancelTimeoutInMinutes }}
templateContext: # Required when this template is hosted in 1ES PT
outputs:
- output: pipelineArtifact

View File

@@ -57,4 +57,5 @@ stages:
useLatestWinAppSDK: ${{ parameters.useLatestWinAppSDK }}
${{ if eq(parameters.useLatestWinAppSDK, true) }}:
winAppSDKVersionNumber: ${{ parameters.winAppSDKVersionNumber }}
useExperimentalVersion: ${{ parameters.useExperimentalVersion }}
useExperimentalVersion: ${{ parameters.useExperimentalVersion }}
timeoutInMinutes: 90

View File

@@ -56,6 +56,7 @@ stages:
runTests: false
buildTests: true
useVSPreview: ${{ parameters.useVSPreview }}
timeoutInMinutes: 90
- ${{ if eq(parameters.useLatestOfficialBuild, true) }}:
- stage: BuildUITests_${{ platform }}

View File

@@ -47,6 +47,8 @@ Contact the developers of a plugin directly for assistance with a specific plugi
| [Weather](https://github.com/ruslanlap/PowerToysRun-Weather) | [ruslanlap](https://github.com/ruslanlap) | Get real-time weather information directly from PowerToys Run. |
| [Pomodoro](https://github.com/ruslanlap/PowerToysRun-Pomodoro) | [ruslanlap](https://github.com/ruslanlap) | Manage Pomodoro productivity sessions directly from PowerToys Run. |
| [Definition](https://github.com/ruslanlap/PowerToysRun-Definition) | [ruslanlap](https://github.com/ruslanlap) | Lookup word definitions, phonetics, and synonyms directly in PowerToys Run. |
| [Hotkeys](https://github.com/ruslanlap/PowerToysRun-Hotkeys) | [ruslanlap](https://github.com/ruslanlap) | Create, manage, and trigger custom keyboard shortcuts directly from PowerToys Run. |
| [RandomGen](https://github.com/ruslanlap/PowerToysRun-RandomGen) | [ruslanlap](https://github.com/ruslanlap) | 🎲 Generate random data instantly with a single keystroke. Perfect for developers, testers, designers, and anyone who needs quick access to random data. Features include secure passwords, PINs, names, business data, dates, numbers, GUIDs, color codes, and more. Especially useful for designers who need random color codes and placeholder content. |
## Extending software plugins

View File

@@ -233,15 +233,6 @@
</RegistryKey>
<File Id="GcodePreviewHandler_$(var.IdSafeLanguage)_File" Source="$(var.BinDir)\$(var.Language)\PowerToys.GcodePreviewHandler.resources.dll" />
</Component>
<Component
Id="BgcodePreviewHandler_$(var.IdSafeLanguage)_Component"
Directory="Resource$(var.IdSafeLanguage)INSTALLFOLDER"
Guid="$(var.CompGUIDPrefix)07">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="BgcodePreviewHandler_$(var.IdSafeLanguage)_Component" Value="" KeyPath="yes"/>
</RegistryKey>
<File Id="BgcodePreviewHandler_$(var.IdSafeLanguage)_File" Source="$(var.BinDir)\$(var.Language)\PowerToys.BgcodePreviewHandler.resources.dll" />
</Component>
<!-- PowerToys Run aka Launcher plugin resources -->
<Component
Id="Launcher_Calculator_$(var.IdSafeLanguage)_Component"
@@ -467,6 +458,15 @@
</RegistryKey>
<File Id="WorkspacesEditor_$(var.IdSafeLanguage)_File" Source="$(var.BinDir)\$(var.Language)\PowerToys.WorkspacesEditor.resources.dll" />
</Component>
<Component
Id="BgcodePreviewHandler_$(var.IdSafeLanguage)_Component"
Directory="Resource$(var.IdSafeLanguage)INSTALLFOLDER"
Guid="$(var.CompGUIDPrefix)22">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="BgcodePreviewHandler_$(var.IdSafeLanguage)_Component" Value="" KeyPath="yes"/>
</RegistryKey>
<File Id="BgcodePreviewHandler_$(var.IdSafeLanguage)_File" Source="$(var.BinDir)\$(var.Language)\PowerToys.BgcodePreviewHandler.resources.dll" />
</Component>
<?undef IdSafeLanguage?>
<?undef CompGUIDPrefix?>
<?endforeach?>

View File

@@ -122,23 +122,31 @@ namespace Microsoft.PowerToys.UITest
public void StartExe(string appPath, string[]? args = null)
{
var opts = new AppiumOptions();
opts.AddAdditionalCapability("app", appPath);
if (args != null && args.Length > 0)
if (scope == PowerToysModule.PowerToysSettings)
{
// Build command line arguments string
string argsString = string.Join(" ", args.Select(arg =>
TryLaunchPowerToysSettings(opts);
}
else
{
opts.AddAdditionalCapability("app", appPath);
if (args != null && args.Length > 0)
{
// Quote arguments that contain spaces
if (arg.Contains(' '))
// Build command line arguments string
string argsString = string.Join(" ", args.Select(arg =>
{
return $"\"{arg}\"";
}
// Quote arguments that contain spaces
if (arg.Contains(' '))
{
return $"\"{arg}\"";
}
return arg;
}));
return arg;
}));
opts.AddAdditionalCapability("appArguments", argsString);
opts.AddAdditionalCapability("appArguments", argsString);
}
}
this.Driver = NewWindowsDriver(opts);

View File

@@ -19,13 +19,13 @@ public interface IExtensionService
Task SignalStopExtensionsAsync();
public event TypedEventHandler<IExtensionService, IEnumerable<IExtensionWrapper>>? OnExtensionAdded;
event TypedEventHandler<IExtensionService, IEnumerable<IExtensionWrapper>>? OnExtensionAdded;
public event TypedEventHandler<IExtensionService, IEnumerable<IExtensionWrapper>>? OnExtensionRemoved;
event TypedEventHandler<IExtensionService, IEnumerable<IExtensionWrapper>>? OnExtensionRemoved;
public void EnableExtension(string extensionUniqueId);
void EnableExtension(string extensionUniqueId);
public void DisableExtension(string extensionUniqueId);
void DisableExtension(string extensionUniqueId);
///// <summary>
///// Gets a boolean indicating whether the extension was disabled due to the corresponding Windows optional feature

View File

@@ -0,0 +1,37 @@
// 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.Threading.Tasks;
namespace Microsoft.CmdPal.Common.Services;
public interface IRootPageService
{
/// <summary>
/// Gets the root page of the command palette. Return any IPage implementation that
/// represents the root view of this instance of the command palette.
/// </summary>
Microsoft.CommandPalette.Extensions.IPage GetRootPage();
/// <summary>
/// Pre-loads any necessary data or state before the root page is loaded.
/// This will be awaited before the root page and the user can do anything,
/// so ideally it should be quick and not block the UI thread for long.
/// </summary>
Task PreLoadAsync();
/// <summary>
/// Do any loading work that can be done after the root page is loaded and
/// displayed to the user.
/// This is run asynchronously, on a background thread.
/// </summary>
Task PostLoadRootPageAsync();
/// <summary>
/// Called when a top-level command is performed. The context is the
/// sender context for the invoked command. This is typically the IListItem
/// or ICommandContextItem that was used to invoke the command.
/// </summary>
void OnPerformTopLevelCommand(object? context);
}

View File

@@ -9,11 +9,9 @@ using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using ManagedCommon;
using Microsoft.CmdPal.Common.Services;
using Microsoft.CmdPal.UI.ViewModels.MainPage;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.CmdPal.UI.ViewModels.Models;
using Microsoft.CommandPalette.Extensions;
using Microsoft.Extensions.DependencyInjection;
using WinRT;
namespace Microsoft.CmdPal.UI.ViewModels;
@@ -21,7 +19,7 @@ namespace Microsoft.CmdPal.UI.ViewModels;
public partial class ShellViewModel : ObservableObject,
IRecipient<PerformCommandMessage>
{
private readonly IServiceProvider _serviceProvider;
private readonly IRootPageService _rootPageService;
private readonly TaskScheduler _scheduler;
private readonly Lock _invokeLock = new();
private Task? _handleInvokeTask;
@@ -60,17 +58,17 @@ public partial class ShellViewModel : ObservableObject,
}
}
private MainListPage? _mainListPage;
private IPage? _rootPage;
private IExtensionWrapper? _activeExtension;
private bool _isNested;
public bool IsNested { get => _isNested; }
public ShellViewModel(IServiceProvider serviceProvider, TaskScheduler scheduler)
public ShellViewModel(TaskScheduler scheduler, IRootPageService rootPageService)
{
_serviceProvider = serviceProvider;
_scheduler = scheduler;
_rootPageService = rootPageService;
_currentPage = new LoadingPageViewModel(null, _scheduler);
// Register to receive messages
@@ -80,24 +78,27 @@ public partial class ShellViewModel : ObservableObject,
[RelayCommand]
public async Task<bool> LoadAsync()
{
var tlcManager = _serviceProvider.GetService<TopLevelCommandManager>();
await tlcManager!.LoadBuiltinsAsync();
// First, do any loading that the root page service needs to do before we can
// display the root page. For example, this might include loading
// the built-in commands, or loading the settings.
await _rootPageService.PreLoadAsync();
IsLoaded = true;
// Built-ins have loaded. We can display our page at this point.
_mainListPage = new MainListPage(_serviceProvider);
WeakReferenceMessenger.Default.Send<PerformCommandMessage>(new(new ExtensionObject<ICommand>(_mainListPage)));
// Now that the basics are set up, we can load the root page.
_rootPage = _rootPageService.GetRootPage();
// This sends a message to us to load the root page view model.
WeakReferenceMessenger.Default.Send<PerformCommandMessage>(new(new ExtensionObject<ICommand>(_rootPage)));
// Now that the root page is loaded, do any post-load work that the root page service needs to do.
// This runs asynchronously, on a background thread.
// This might include starting extensions, for example.
// Note: We don't await this, so that we can return immediately.
// This is important because we don't want to block the UI thread.
_ = Task.Run(async () =>
{
// After loading built-ins, and starting navigation, kick off a thread to load extensions.
tlcManager.LoadExtensionsCommand.Execute(null);
await tlcManager.LoadExtensionsCommand.ExecutionTask!;
if (tlcManager.LoadExtensionsCommand.ExecutionTask.Status != TaskStatus.RanToCompletion)
{
// TODO: Handle failure case
}
await _rootPageService.PostLoadRootPageAsync();
});
return true;
@@ -172,15 +173,7 @@ public partial class ShellViewModel : ObservableObject,
public void PerformTopLevelCommand(PerformCommandMessage message)
{
if (_mainListPage == null)
{
return;
}
if (message.Context is IListItem listItem)
{
_mainListPage.UpdateHistory(listItem);
}
_rootPageService.OnPerformTopLevelCommand(message.Context);
}
public void Receive(PerformCommandMessage message)
@@ -255,10 +248,11 @@ public partial class ShellViewModel : ObservableObject,
{
Logger.LogDebug($"Navigating to page");
var isMainPage = command is MainListPage;
var isMainPage = command == _rootPage;
_isNested = !isMainPage;
// Construct our ViewModel of the appropriate type and pass it the UI Thread context.
var pageViewModel = GetViewModelForPage(page, !isMainPage, host);
var pageViewModel = GetViewModelForPage(page, _isNested, host);
if (pageViewModel == null)
{
Logger.LogError($"Failed to create ViewModel for page {page.GetType().Name}");
@@ -267,8 +261,6 @@ public partial class ShellViewModel : ObservableObject,
// Kick off async loading of our ViewModel
LoadPageViewModel(pageViewModel);
_isNested = !isMainPage;
OnUIThread(() => { WeakReferenceMessenger.Default.Send<UpdateCommandBarMessage>(new(null)); });
WeakReferenceMessenger.Default.Send<NavigateToPageMessage>(new(pageViewModel, message.WithAnimation));

View File

@@ -144,6 +144,7 @@ public partial class App : Application
services.AddSingleton<IExtensionService, ExtensionService>();
services.AddSingleton<TrayIconService>();
services.AddSingleton<IRootPageService, PowerToysRootPageService>();
services.AddSingleton(new TelemetryForwarder());
// ViewModels

View File

@@ -162,10 +162,6 @@ public sealed partial class SearchBar : UserControl,
CurrentPageViewModel.Filter = FilterBox.Text;
}
}
else if (e.Key == VirtualKey.Left && altPressed)
{
WeakReferenceMessenger.Default.Send<NavigateBackMessage>(new());
}
if (!e.Handled)
{

View File

@@ -107,7 +107,7 @@ public sealed partial class MainWindow : WindowEx,
App.Current.Services.GetService<SettingsModel>()!.SettingsChanged += SettingsChangedHandler;
// Make sure that we update the acrylic theme when the OS theme changes
RootShellPage.ActualThemeChanged += (s, e) => UpdateAcrylic();
RootShellPage.ActualThemeChanged += (s, e) => DispatcherQueue.TryEnqueue(UpdateAcrylic);
// Hardcoding event name to avoid bringing in the PowerToys.interop dependency. Event name must match CMDPAL_SHOW_EVENT from shared_constants.h
NativeEventWaiter.WaitForEventLoop("Local\\PowerToysCmdPal-ShowEvent-62336fcd-8611-4023-9b30-091a6af4cc5a", () =>
@@ -175,6 +175,8 @@ public sealed partial class MainWindow : WindowEx,
private void UpdateAcrylic()
{
_acrylicController?.RemoveAllSystemBackdropTargets();
_acrylicController = GetAcrylicConfig(Content);
// Enable the system backdrop.

View File

@@ -20,6 +20,7 @@ using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media.Animation;
using DispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue;
using VirtualKey = Windows.System.VirtualKey;
namespace Microsoft.CmdPal.UI.Pages;
@@ -81,6 +82,7 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
WeakReferenceMessenger.Default.Register<ShowToastMessage>(this);
WeakReferenceMessenger.Default.Register<NavigateToPageMessage>(this);
AddHandler(PreviewKeyDownEvent, new KeyEventHandler(ShellPage_OnPreviewKeyDown), true);
AddHandler(PointerPressedEvent, new PointerEventHandler(ShellPage_OnPointerPressed), true);
RootFrame.Navigate(typeof(LoadingPage), ViewModel);
@@ -447,6 +449,14 @@ public sealed partial class ShellPage : Microsoft.UI.Xaml.Controls.Page,
}
}
private void ShellPage_OnPreviewKeyDown(object sender, KeyRoutedEventArgs e)
{
if (e.Key == VirtualKey.Left && e.KeyStatus.IsMenuKeyDown)
{
WeakReferenceMessenger.Default.Send<NavigateBackMessage>(new());
}
}
private void ShellPage_OnPointerPressed(object sender, PointerRoutedEventArgs e)
{
try

View File

@@ -0,0 +1,71 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using ManagedCommon;
using Microsoft.CmdPal.Common.Services;
using Microsoft.CmdPal.UI.ViewModels;
using Microsoft.CmdPal.UI.ViewModels.MainPage;
using Microsoft.CommandPalette.Extensions;
using Microsoft.Extensions.DependencyInjection;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace Microsoft.CmdPal.UI;
internal sealed class PowerToysRootPageService : IRootPageService
{
private readonly IServiceProvider _serviceProvider;
private Lazy<MainListPage> _mainListPage;
public PowerToysRootPageService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_mainListPage = new Lazy<MainListPage>(() =>
{
return new MainListPage(_serviceProvider);
});
}
public async Task PreLoadAsync()
{
var tlcManager = _serviceProvider.GetService<TopLevelCommandManager>()!;
await tlcManager.LoadBuiltinsAsync();
}
public Microsoft.CommandPalette.Extensions.IPage GetRootPage()
{
return _mainListPage.Value;
}
public async Task PostLoadRootPageAsync()
{
var tlcManager = _serviceProvider.GetService<TopLevelCommandManager>()!;
// After loading built-ins, and starting navigation, kick off a thread to load extensions.
tlcManager.LoadExtensionsCommand.Execute(null);
await tlcManager.LoadExtensionsCommand.ExecutionTask!;
if (tlcManager.LoadExtensionsCommand.ExecutionTask.Status != TaskStatus.RanToCompletion)
{
// TODO: Handle failure case
}
}
public void OnPerformTopLevelCommand(object? context)
{
try
{
if (context is IListItem listItem)
{
_mainListPage.Value.UpdateHistory(listItem);
}
}
catch (Exception ex)
{
Logger.LogError("Failed to update history in PowerToysRootPageService");
Logger.LogError(ex.ToString());
}
}
}

View File

@@ -32,6 +32,8 @@ internal sealed partial class FallbackOpenURLItem : FallbackCommandItem
{
if (!IsValidUrl(query))
{
_executeItem.Url = string.Empty;
_executeItem.Name = string.Empty;
Title = string.Empty;
Subtitle = string.Empty;
return;

View File

@@ -23,28 +23,31 @@ internal sealed partial class WebSearchListPage : DynamicListPage
private readonly SettingsManager _settingsManager;
private static readonly CompositeFormat PluginInBrowserName = System.Text.CompositeFormat.Parse(Properties.Resources.plugin_in_browser_name);
private static readonly CompositeFormat PluginOpen = System.Text.CompositeFormat.Parse(Properties.Resources.plugin_open);
private List<ListItem> allItems;
private List<ListItem> _allItems;
public WebSearchListPage(SettingsManager settingsManager)
{
Name = Resources.command_item_title;
Title = Resources.command_item_title;
PlaceholderText = Resources.plugin_description;
Icon = IconHelpers.FromRelativePath("Assets\\WebSearch.png");
allItems = [new(new NoOpCommand())
{
Icon = IconHelpers.FromRelativePath("Assets\\WebSearch.png"),
Title = Properties.Resources.plugin_description,
Subtitle = string.Format(CultureInfo.CurrentCulture, PluginOpen, BrowserInfo.Name ?? BrowserInfo.MSEdgeName),
}
];
_allItems = [];
Id = "com.microsoft.cmdpal.websearch";
_settingsManager = settingsManager;
_historyItems = _settingsManager.ShowHistory != Resources.history_none ? _settingsManager.LoadHistory() : null;
if (_historyItems != null)
{
allItems.AddRange(_historyItems);
_allItems.AddRange(_historyItems);
}
// It just looks viewer to have string twice on the page, and default placeholder is good enough
PlaceholderText = _allItems.Count > 0 ? Resources.plugin_description : string.Empty;
EmptyContent = new CommandItem(new NoOpCommand())
{
Icon = Icon,
Title = Properties.Resources.plugin_description,
Subtitle = string.Format(CultureInfo.CurrentCulture, PluginInBrowserName, BrowserInfo.Name ?? BrowserInfo.MSEdgeName),
};
}
public List<ListItem> Query(string query)
@@ -59,17 +62,7 @@ internal sealed partial class WebSearchListPage : DynamicListPage
var results = new List<ListItem>();
// empty query
if (string.IsNullOrEmpty(query))
{
results.Add(new ListItem(new SearchWebCommand(string.Empty, _settingsManager))
{
Title = Properties.Resources.plugin_description,
Subtitle = string.Format(CultureInfo.CurrentCulture, PluginInBrowserName, BrowserInfo.Name ?? BrowserInfo.MSEdgeName),
Icon = new IconInfo(_iconPath),
});
}
else
if (!string.IsNullOrEmpty(query))
{
var searchTerm = query;
var result = new ListItem(new SearchWebCommand(searchTerm, _settingsManager))
@@ -91,9 +84,9 @@ internal sealed partial class WebSearchListPage : DynamicListPage
public override void UpdateSearchText(string oldSearch, string newSearch)
{
allItems = [.. Query(newSearch)];
_allItems = [.. Query(newSearch)];
RaiseItemsChanged(0);
}
public override IListItem[] GetItems() => [.. allItems];
public override IListItem[] GetItems() => [.. _allItems];
}

View File

@@ -90,14 +90,12 @@ namespace Peek.FilePreviewer.Previewers
unsafe
{
// This runs the preview handler in a separate process (prevhost.exe)
// TODO: Figure out how to get it to run in a low integrity level
if (!HandlerFactories.TryGetValue(clsid, out var factory))
{
var hr = PInvoke_FilePreviewer.CoGetClassObject(clsid, CLSCTX.CLSCTX_LOCAL_SERVER, null, typeof(IClassFactory).GUID, out object pFactory);
Marshal.ThrowExceptionForHR(hr);
// Storing the factory in memory helps makes the handlers load faster
// TODO: Maybe free them after some inactivity or when Peek quits?
factory = (IClassFactory)pFactory;
factory.LockServer(true);
HandlerFactories.AddOrUpdate(clsid, factory, (_, _) => factory);
@@ -213,6 +211,20 @@ namespace Peek.FilePreviewer.Previewers
return !string.IsNullOrEmpty(GetPreviewHandlerGuid(item.Extension));
}
public static void ReleaseHandlerFactories()
{
foreach (var factory in HandlerFactories.Values)
{
try
{
Marshal.FinalReleaseComObject(factory);
}
catch
{
}
}
}
private static string? GetPreviewHandlerGuid(string fileExt)
{
const string PreviewHandlerKeyPath = "shellex\\{8895b1c6-b41f-4c1c-a562-0d564250836f}";

View File

@@ -12,6 +12,7 @@ using Microsoft.UI.Xaml;
using Peek.Common;
using Peek.FilePreviewer;
using Peek.FilePreviewer.Models;
using Peek.FilePreviewer.Previewers;
using Peek.UI.Native;
using Peek.UI.Telemetry.Events;
using Peek.UI.Views;
@@ -111,6 +112,7 @@ namespace Peek.UI
NativeEventWaiter.WaitForEventLoop(Constants.ShowPeekEvent(), OnPeekHotkey);
NativeEventWaiter.WaitForEventLoop(Constants.TerminatePeekEvent(), () =>
{
ShellPreviewHandlerPreviewer.ReleaseHandlerFactories();
EtwTrace?.Dispose();
Environment.Exit(0);
});

View File

@@ -15,6 +15,7 @@ using Microsoft.UI.Xaml.Input;
using Peek.Common.Constants;
using Peek.Common.Extensions;
using Peek.FilePreviewer.Models;
using Peek.FilePreviewer.Previewers;
using Peek.UI.Extensions;
using Peek.UI.Helpers;
using Peek.UI.Telemetry.Events;
@@ -204,6 +205,8 @@ namespace Peek.UI
ViewModel.ScalingFactor = 1;
this.Content.KeyUp -= Content_KeyUp;
ShellPreviewHandlerPreviewer.ReleaseHandlerFactories();
}
/// <summary>

View File

@@ -405,13 +405,19 @@ public:
{
ResetEvent(m_hInvokeEvent);
SetEvent(m_hTerminateEvent);
WaitForSingleObject(m_hProcess, 1500);
auto result = TerminateProcess(m_hProcess, 1);
if (result == 0)
HANDLE hProcess = OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, FALSE, m_processPid);
if (WaitForSingleObject(hProcess, 1500) == WAIT_TIMEOUT)
{
int error = GetLastError();
Logger::trace("Couldn't terminate the process. Last error: {}", error);
auto result = TerminateProcess(hProcess, 1);
if (result == 0)
{
int error = GetLastError();
Logger::trace("Couldn't terminate the process. Last error: {}", error);
}
}
CloseHandle(hProcess);
CloseHandle(m_hProcess);
m_hProcess = 0;
m_processPid = 0;

View File

@@ -51,6 +51,7 @@ namespace PowerAccent.Core
SR_CYRL,
SV,
TK,
VI,
}
internal sealed class Languages
@@ -113,6 +114,7 @@ namespace PowerAccent.Core
Language.SR_CYRL => GetDefaultLetterKeySRCyrillic(letter), // Serbian Cyrillic
Language.SV => GetDefaultLetterKeySV(letter), // Swedish
Language.TK => GetDefaultLetterKeyTK(letter), // Turkish
Language.VI => GetDefaultLetterKeyVI(letter), // Vietnamese
_ => throw new ArgumentException("The language {0} is not known in this context", lang.ToString()),
});
}
@@ -168,6 +170,7 @@ namespace PowerAccent.Core
.Union(GetDefaultLetterKeySRCyrillic(letter))
.Union(GetDefaultLetterKeySV(letter))
.Union(GetDefaultLetterKeyTK(letter))
.Union(GetDefaultLetterKeyVI(letter))
.Union(GetDefaultLetterKeySPECIAL(letter))
.ToArray();
@@ -890,6 +893,22 @@ namespace PowerAccent.Core
};
}
// Vietnamese
private static string[] GetDefaultLetterKeyVI(LetterKey letter)
{
return letter switch
{
LetterKey.VK_A => new[] { "à", "ả", "ã", "á", "ạ", "ă", "ằ", "ẳ", "ẵ", "ắ", "ặ", "â", "ầ", "ẩ", "ẫ", "ấ", "ậ" },
LetterKey.VK_D => new[] { "đ" },
LetterKey.VK_E => new[] { "è", "ẻ", "ẽ", "é", "ẹ", "ê", "ề", "ể", "ễ", "ế", "ệ" },
LetterKey.VK_I => new[] { "ì", "ỉ", "ĩ", "í", "ị" },
LetterKey.VK_O => new[] { "ò", "ỏ", "õ", "ó", "ọ", "ô", "ồ", "ổ", "ỗ", "ố", "ộ", "ơ", "ờ", "ở", "ỡ", "ớ", "ợ" },
LetterKey.VK_U => new[] { "ù", "ủ", "ũ", "ú", "ụ", "ư", "ừ", "ử", "ữ", "ứ", "ự" },
LetterKey.VK_Y => new[] { "ỳ", "ỷ", "ỹ", "ý", "ỵ" },
_ => Array.Empty<string>(),
};
}
// IPA (International Phonetic Alphabet)
private static string[] GetDefaultLetterKeyIPA(LetterKey letter)
{

View File

@@ -3566,6 +3566,9 @@ Activate by holding the key for the character you want to add an accent to, then
<data name="QuickAccent_SelectedLanguage_Turkish" xml:space="preserve">
<value>Turkish</value>
</data>
<data name="QuickAccent_SelectedLanguage_Vietnamese" xml:space="preserve">
<value>Vietnamese</value>
</data>
<data name="QuickAccent_SelectedLanguage_Icelandic" xml:space="preserve">
<value>Icelandic</value>
</data>

View File

@@ -67,6 +67,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
new PowerAccentLanguageModel("SR_CYRL", "QuickAccent_SelectedLanguage_Serbian_Cyrillic", LanguageGroup),
new PowerAccentLanguageModel("SV", "QuickAccent_SelectedLanguage_Swedish", LanguageGroup),
new PowerAccentLanguageModel("TK", "QuickAccent_SelectedLanguage_Turkish", LanguageGroup),
new PowerAccentLanguageModel("VI", "QuickAccent_SelectedLanguage_Vietnamese", LanguageGroup),
new PowerAccentLanguageModel("CY", "QuickAccent_SelectedLanguage_Welsh", LanguageGroup),
];