mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-03 17:56:44 +02:00
<!-- 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 pull request introduces the new Quick Access feature to PowerToys by integrating its host process management into the runner and system tray. The changes add the Quick Access host implementation, update project and build files to include it, and modify the runner and tray icon logic to launch and interact with the Quick Access UI. <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [x] Closes: #43694 <!-- - [ ] Closes: #yyy (add separate lines for additional resolved issues) --> - [x] **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 - [ ] **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 <img width="290" height="420" alt="image" src="https://github.com/user-attachments/assets/7390a706-171c-479f-a4a2-999b18cfc65f" /> <img width="290" height="420" alt="image" src="https://github.com/user-attachments/assets/99e99bc9-b1a3-46c6-b648-81e3048dec1b" /> <img width="490" height="350" alt="image" src="https://github.com/user-attachments/assets/2cce4ad6-a54e-4587-87b7-fdc7fba1f54f" /> <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed --------- Signed-off-by: Shawn Yuan (from Dev Box) <shuaiyuan@microsoft.com> Signed-off-by: Shuai Yuan <shuai.yuan.zju@gmail.com> Co-authored-by: Niels Laute <niels.laute@live.nl>
235 lines
8.2 KiB
C#
235 lines
8.2 KiB
C#
// Copyright (c) Microsoft Corporation
|
|
// The Microsoft Corporation licenses this file to you under the MIT license.
|
|
// See the LICENSE file in the project root for more information.
|
|
|
|
using System;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using ManagedCommon;
|
|
using Microsoft.PowerToys.Settings.UI.Helpers;
|
|
using Microsoft.PowerToys.Settings.UI.Library;
|
|
using Microsoft.PowerToys.Settings.UI.ViewModels;
|
|
using Microsoft.UI.Xaml;
|
|
using Microsoft.UI.Xaml.Controls;
|
|
using Windows.Data.Json;
|
|
|
|
namespace Microsoft.PowerToys.Settings.UI.Views
|
|
{
|
|
/// <summary>
|
|
/// General Settings Page.
|
|
/// </summary>
|
|
public sealed partial class GeneralPage : NavigablePage, IRefreshablePage
|
|
{
|
|
private static DateTime OkToHideBackupAndRestoreMessageTime { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets view model.
|
|
/// </summary>
|
|
public GeneralViewModel ViewModel { get; set; }
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="GeneralPage"/> class.
|
|
/// General Settings page constructor.
|
|
/// </summary>
|
|
public GeneralPage()
|
|
{
|
|
InitializeComponent();
|
|
|
|
// Load string resources
|
|
var loader = Helpers.ResourceLoaderInstance.ResourceLoader;
|
|
var settingsUtils = SettingsUtils.Default;
|
|
|
|
Action stateUpdatingAction = () =>
|
|
{
|
|
this.DispatcherQueue.TryEnqueue(() =>
|
|
{
|
|
ViewModel.RefreshUpdatingState();
|
|
});
|
|
};
|
|
|
|
Action hideBackupAndRestoreMessageArea = () =>
|
|
{
|
|
this.DispatcherQueue.TryEnqueue(async () =>
|
|
{
|
|
const int messageShowTimeIs = 10000;
|
|
|
|
// in order to keep the message for about 5 seconds after the last call
|
|
// and not need any lock/thread-synch, use an OK-To-Hide time, and wait just a little longer than that.
|
|
OkToHideBackupAndRestoreMessageTime = DateTime.UtcNow.AddMilliseconds(messageShowTimeIs - 16);
|
|
await System.Threading.Tasks.Task.Delay(messageShowTimeIs);
|
|
if (DateTime.UtcNow > OkToHideBackupAndRestoreMessageTime)
|
|
{
|
|
ViewModel.HideBackupAndRestoreMessageArea();
|
|
}
|
|
});
|
|
};
|
|
|
|
var doRefreshBackupRestoreStatus = new Action<int>(RefreshBackupRestoreStatus);
|
|
|
|
ViewModel = new GeneralViewModel(
|
|
SettingsRepository<GeneralSettings>.GetInstance(settingsUtils),
|
|
loader.GetString("GeneralSettings_RunningAsAdminText"),
|
|
loader.GetString("GeneralSettings_RunningAsUserText"),
|
|
ShellPage.IsElevated,
|
|
ShellPage.IsUserAnAdmin,
|
|
ShellPage.SendDefaultIPCMessage,
|
|
ShellPage.SendRestartAdminIPCMessage,
|
|
ShellPage.SendCheckForUpdatesIPCMessage,
|
|
string.Empty,
|
|
stateUpdatingAction,
|
|
hideBackupAndRestoreMessageArea,
|
|
doRefreshBackupRestoreStatus,
|
|
PickSingleFolderDialog,
|
|
loader);
|
|
|
|
DataContext = ViewModel;
|
|
|
|
ViewModel.InitializeReportBugLink();
|
|
|
|
// Register IPC handler for bug report status
|
|
ShellPage.ShellHandler.IPCResponseHandleList.Add(HandleBugReportStatusResponse); // Register cleanup on unload
|
|
this.Unloaded += GeneralPage_Unloaded;
|
|
|
|
CheckBugReportStatus();
|
|
|
|
doRefreshBackupRestoreStatus(100);
|
|
|
|
this.Loaded += (s, e) => ViewModel.OnPageLoaded();
|
|
}
|
|
|
|
private void OpenColorsSettings_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
Helpers.StartProcessHelper.Start(Helpers.StartProcessHelper.ColorsSettings);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogError("Error while trying to open the system color settings", ex);
|
|
}
|
|
}
|
|
|
|
private void OpenDiagnosticsAndFeedbackSettings_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
Helpers.StartProcessHelper.Start(Helpers.StartProcessHelper.DiagnosticsAndFeedback);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogError("Error while trying to open the system Diagnostics & Feedback settings", ex);
|
|
}
|
|
}
|
|
|
|
private void RefreshBackupRestoreStatus(int delayMs = 0)
|
|
{
|
|
Task.Run(() =>
|
|
{
|
|
if (delayMs > 0)
|
|
{
|
|
Thread.Sleep(delayMs);
|
|
}
|
|
|
|
var settingsBackupAndRestoreUtils = SettingsBackupAndRestoreUtils.Instance;
|
|
var results = settingsBackupAndRestoreUtils.DryRunBackup();
|
|
this.DispatcherQueue.TryEnqueue(() =>
|
|
{
|
|
ViewModel.NotifyAllBackupAndRestoreProperties();
|
|
});
|
|
});
|
|
}
|
|
|
|
private void UpdateBackupAndRestoreStatusText(Microsoft.UI.Xaml.Documents.Hyperlink sender, Microsoft.UI.Xaml.Documents.HyperlinkClickEventArgs args)
|
|
{
|
|
RefreshBackupRestoreStatus();
|
|
}
|
|
|
|
private async Task<string> PickSingleFolderDialog()
|
|
{
|
|
// This function was changed to use the shell32 API to open folder dialog
|
|
// as the old one (PickSingleFolderAsync) can't work when the process is elevated
|
|
// TODO: go back PickSingleFolderAsync when it's fixed
|
|
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(App.GetSettingsWindow());
|
|
string r = await Task.FromResult<string>(ShellGetFolder.GetFolderDialog(hwnd));
|
|
return r;
|
|
}
|
|
|
|
private void Click_LanguageRestart(object sender, RoutedEventArgs e)
|
|
{
|
|
ViewModel.Restart();
|
|
}
|
|
|
|
private void Click_ViewDiagnosticDataViewerRestart(object sender, RoutedEventArgs e)
|
|
{
|
|
ViewModel.Restart();
|
|
}
|
|
|
|
public void RefreshEnabledState()
|
|
{
|
|
ViewModel.RefreshSettingsOnExternalChange();
|
|
}
|
|
|
|
private async void ViewDiagnosticData_Click(object sender, RoutedEventArgs e)
|
|
{
|
|
await Task.Run(ViewModel.ViewDiagnosticData);
|
|
}
|
|
|
|
private void BugReportToolClicked(object sender, RoutedEventArgs e)
|
|
{
|
|
// Start bug report
|
|
ShellPage.SendDefaultIPCMessage("{\"bugreport\": 0 }");
|
|
|
|
ViewModel.IsBugReportRunning = true;
|
|
|
|
// No need to start timer - the observer pattern will notify us when it finishes
|
|
}
|
|
|
|
private void CheckBugReportStatus()
|
|
{
|
|
// Send one-time request to check current bug report status
|
|
string ipcMessage = "{ \"bug_report_status\": { } }";
|
|
ShellPage.SendDefaultIPCMessage(ipcMessage);
|
|
}
|
|
|
|
private void HandleBugReportStatusResponse(JsonObject response)
|
|
{
|
|
if (response.ContainsKey("bug_report_running"))
|
|
{
|
|
var isRunning = response.GetNamedBoolean("bug_report_running");
|
|
|
|
// Update UI on the UI thread
|
|
this.DispatcherQueue.TryEnqueue(() =>
|
|
{
|
|
ViewModel.IsBugReportRunning = isRunning;
|
|
});
|
|
}
|
|
}
|
|
|
|
private void GeneralPage_Unloaded(object sender, RoutedEventArgs e)
|
|
{
|
|
CleanupBugReportHandlers();
|
|
}
|
|
|
|
private void CleanupBugReportHandlers()
|
|
{
|
|
// Remove IPC handler
|
|
if (ShellPage.ShellHandler?.IPCResponseHandleList != null)
|
|
{
|
|
ShellPage.ShellHandler.IPCResponseHandleList.Remove(HandleBugReportStatusResponse);
|
|
}
|
|
}
|
|
|
|
private void ShowSystemTrayIcon_Toggled(object sender, RoutedEventArgs e)
|
|
{
|
|
if (sender is ToggleSwitch toggleSwitch)
|
|
{
|
|
var shellViewModel = ShellPage.ShellHandler?.ViewModel;
|
|
if (shellViewModel != null)
|
|
{
|
|
shellViewModel.ShowCloseMenu = !toggleSwitch.IsOn;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|