mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 10:46:33 +02:00
[Feature] PowerToys hotkey conflict detection (#41029)
<!-- 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 Implements comprehensive hotkey conflict detection and resolution system for PowerToys, providing real-time conflict checking and centralized management interface. ## PR Checklist - [ ] **Closes:** #xxx - [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:** 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) - [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: [Shortcut conflict detction dev spec](https://github.com/MicrosoftDocs/windows-dev-docs/pull/5519) ## TODO Lists - [x] Add real-time hotkey validation functionality to the hotkey dialog - [x] Immediately detect conflicts and update shortcut conflict status after applying new shortcuts - [x] Return conflict list from runner hotkey conflict detector for conflict checking. - [x] Implement the Tooltip for every shortcut control - [x] Add dialog UI for showing all the shortcut conflicts - [x] Support changing shortcut directly inside the shortcut conflict window/dialog, no need to nav to the settings page. - [x] Redesign the `ShortcutConflictDialogContentControl` to align with the spec - [x] Add navigating and changing hotkey auctionability to the `ShortcutConflictDialogContentControl` - [x] Add telemetry. Impemented in [another PR](https://github.com/shuaiyuanxx/PowerToys/pull/47) ## Shortcut Conflict Support Modules  <details> <summary>Demo videos</summary> https://github.com/user-attachments/assets/476d992c-c6ca-4bcd-a3f2-b26cc612d1b9 https://github.com/user-attachments/assets/1c1a2537-de54-4db2-bdbf-6f1908ff1ce7 https://github.com/user-attachments/assets/9c992254-fc2b-402c-beec-20fceef25e6b https://github.com/user-attachments/assets/d66abc1c-b8bf-45f8-a552-ec989dab310f </details> <!-- 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 Manually validation performed. --------- Signed-off-by: Shawn Yuan <shuaiyuan@microsoft.com> Signed-off-by: Shuai Yuan <shuai.yuan.zju@gmail.com> Co-authored-by: Niels Laute <niels.laute@live.nl>
This commit is contained in:
@@ -20,33 +20,56 @@
|
||||
</HyperlinkButton>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
Orientation="Vertical"
|
||||
Spacing="8"
|
||||
Visibility="{x:Bind ShowDataDiagnosticsSetting, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<StackPanel Orientation="Vertical" Visibility="{x:Bind ShowDataDiagnosticsSetting, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<TextBlock
|
||||
x:Uid="Oobe_Overview_Telemetry_Title"
|
||||
Margin="0,20,0,0"
|
||||
Style="{StaticResource SubtitleTextBlockStyle}" />
|
||||
Margin="0,24,0,0"
|
||||
Style="{StaticResource BodyStrongTextBlockStyle}" />
|
||||
<TextBlock x:Uid="Oobe_Overview_Telemetry_Desc" Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
|
||||
<tkcontrols:SettingsCard
|
||||
x:Uid="Oobe_Overview_EnableDataDiagnostics"
|
||||
Margin="0,8,0,0"
|
||||
HeaderIcon="{ui:FontIcon Glyph=}"
|
||||
IsEnabled="{x:Bind ShowDataDiagnosticsSetting, Mode=OneWay}">
|
||||
<tkcontrols:SettingsCard.Description>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock
|
||||
x:Uid="GeneralPage_EnableDataDiagnosticsText"
|
||||
Style="{StaticResource SecondaryTextStyle}"
|
||||
TextWrapping="WrapWholeWords" />
|
||||
<HyperlinkButton
|
||||
x:Uid="GeneralPage_DiagnosticsAndFeedback_Link"
|
||||
Margin="0,2,0,0"
|
||||
FontWeight="SemiBold"
|
||||
NavigateUri="https://aka.ms/powertoys-data-and-privacy-documentation" />
|
||||
<HyperlinkButton
|
||||
x:Uid="Oobe_Overview_DiagnosticsAndFeedback_Settings_Link"
|
||||
Margin="0,2,0,0"
|
||||
Click="GeneralSettingsLaunchButton_Click"
|
||||
FontWeight="SemiBold" />
|
||||
</StackPanel>
|
||||
</tkcontrols:SettingsCard.Description>
|
||||
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind EnableDataDiagnostics, Mode=TwoWay}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
|
||||
<HyperlinkButton
|
||||
x:Uid="Oobe_Overview_DiagnosticsAndFeedback_Settings_Link"
|
||||
Margin="-8,0,0,0"
|
||||
Click="GeneralSettingsLaunchButton_Click" />
|
||||
|
||||
<HyperlinkButton
|
||||
x:Uid="Oobe_Overview_DiagnosticsAndFeedback_Link"
|
||||
Margin="-8,0,0,0"
|
||||
NavigateUri="https://aka.ms/powertoys-data-and-privacy-documentation" />
|
||||
<!-- Show title and description only when there are conflicts -->
|
||||
<TextBlock
|
||||
x:Uid="Oobe_Overview_Hotkey_Conflict_Title"
|
||||
Margin="0,24,0,8"
|
||||
Style="{StaticResource BodyStrongTextBlockStyle}" />
|
||||
|
||||
<!-- Always show shortcut status card -->
|
||||
<tkcontrols:SettingsCard Description="{x:Bind ConflictDescription, Mode=OneWay}" Header="{x:Bind ConflictText, Mode=OneWay}">
|
||||
<tkcontrols:SettingsCard.HeaderIcon>
|
||||
<FontIcon Foreground="{x:Bind IconForeground, Mode=OneWay}" Glyph="{x:Bind IconGlyph, Mode=OneWay}" />
|
||||
</tkcontrols:SettingsCard.HeaderIcon>
|
||||
<!-- Only show button when there are conflicts -->
|
||||
<Button
|
||||
x:Uid="ResolveConflicts_Button"
|
||||
Click="ShortcutConflictBtn_Click"
|
||||
Visibility="{x:Bind HasConflicts, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
</tkcontrols:SettingsCard>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</controls:OOBEPageControl.PageContent>
|
||||
|
||||
@@ -2,21 +2,32 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.ComponentModel;
|
||||
using global::PowerToys.GPOWrapper;
|
||||
using Microsoft.PowerToys.Settings.UI.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.HotkeyConflicts;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
using Microsoft.PowerToys.Settings.UI.OOBE.Enums;
|
||||
using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel;
|
||||
using Microsoft.PowerToys.Settings.UI.Services;
|
||||
using Microsoft.PowerToys.Settings.UI.SettingsXAML.Controls.Dashboard;
|
||||
using Microsoft.PowerToys.Settings.UI.Views;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
||||
{
|
||||
public sealed partial class OobeOverview : Page
|
||||
public sealed partial class OobeOverview : Page, INotifyPropertyChanged
|
||||
{
|
||||
public OobePowerToysModule ViewModel { get; set; }
|
||||
|
||||
private bool _enableDataDiagnostics;
|
||||
private AllHotkeyConflictsData _allHotkeyConflictsData = new AllHotkeyConflictsData();
|
||||
private Windows.ApplicationModel.Resources.ResourceLoader resourceLoader = Helpers.ResourceLoaderInstance.ResourceLoader;
|
||||
|
||||
public bool EnableDataDiagnostics
|
||||
{
|
||||
@@ -41,8 +52,151 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
||||
}
|
||||
}
|
||||
|
||||
public AllHotkeyConflictsData AllHotkeyConflictsData
|
||||
{
|
||||
get => _allHotkeyConflictsData;
|
||||
set
|
||||
{
|
||||
if (_allHotkeyConflictsData != value)
|
||||
{
|
||||
_allHotkeyConflictsData = value;
|
||||
OnPropertyChanged(nameof(AllHotkeyConflictsData));
|
||||
OnPropertyChanged(nameof(ConflictCount));
|
||||
OnPropertyChanged(nameof(ConflictText));
|
||||
OnPropertyChanged(nameof(ConflictDescription));
|
||||
OnPropertyChanged(nameof(HasConflicts));
|
||||
OnPropertyChanged(nameof(IconGlyph));
|
||||
OnPropertyChanged(nameof(IconForeground));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int ConflictCount
|
||||
{
|
||||
get
|
||||
{
|
||||
if (AllHotkeyConflictsData == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
if (AllHotkeyConflictsData.InAppConflicts != null)
|
||||
{
|
||||
count += AllHotkeyConflictsData.InAppConflicts.Count;
|
||||
}
|
||||
|
||||
if (AllHotkeyConflictsData.SystemConflicts != null)
|
||||
{
|
||||
count += AllHotkeyConflictsData.SystemConflicts.Count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
public string ConflictText
|
||||
{
|
||||
get
|
||||
{
|
||||
var count = ConflictCount;
|
||||
if (count == 0)
|
||||
{
|
||||
// Return no-conflict message
|
||||
try
|
||||
{
|
||||
return resourceLoader.GetString("ShortcutConflictControl_NoConflictsFound");
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "No conflicts found";
|
||||
}
|
||||
}
|
||||
else if (count == 1)
|
||||
{
|
||||
// Try to get localized string
|
||||
try
|
||||
{
|
||||
return resourceLoader.GetString("ShortcutConflictControl_SingleConflictFound");
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "1 shortcut conflict";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to get localized string
|
||||
try
|
||||
{
|
||||
var template = resourceLoader.GetString("ShortcutConflictControl_MultipleConflictsFound");
|
||||
return string.Format(System.Globalization.CultureInfo.CurrentCulture, template, count);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return $"{count} shortcut conflicts";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string ConflictDescription
|
||||
{
|
||||
get
|
||||
{
|
||||
var count = ConflictCount;
|
||||
if (count == 0)
|
||||
{
|
||||
// Return no-conflict description
|
||||
try
|
||||
{
|
||||
return resourceLoader.GetString("ShortcutConflictWindow_NoConflictsDescription");
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "All shortcuts function correctly";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Return conflict description
|
||||
try
|
||||
{
|
||||
return resourceLoader.GetString("Oobe_Overview_Hotkey_Conflict_Card_Description");
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "Shortcuts configured by PowerToys are conflicting";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasConflicts => ConflictCount > 0;
|
||||
|
||||
public string IconGlyph => HasConflicts ? "\uE814" : "\uE73E";
|
||||
|
||||
public SolidColorBrush IconForeground
|
||||
{
|
||||
get
|
||||
{
|
||||
if (HasConflicts)
|
||||
{
|
||||
// Red color for conflicts
|
||||
return (SolidColorBrush)App.Current.Resources["SystemFillColorCriticalBrush"];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Green color for no conflicts
|
||||
return (SolidColorBrush)App.Current.Resources["SystemFillColorSuccessBrush"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool ShowDataDiagnosticsSetting => GetIsDataDiagnosticsInfoBarEnabled();
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private bool GetIsDataDiagnosticsInfoBarEnabled()
|
||||
{
|
||||
var isDataDiagnosticsGpoDisallowed = GPOWrapper.GetAllowDataDiagnosticsValue() == GpoRuleConfigured.Disabled;
|
||||
@@ -57,7 +211,27 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
||||
_enableDataDiagnostics = DataDiagnosticsSettings.GetEnabledValue();
|
||||
|
||||
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.Overview]);
|
||||
DataContext = ViewModel;
|
||||
DataContext = this;
|
||||
|
||||
// Subscribe to hotkey conflict updates
|
||||
if (GlobalHotkeyConflictManager.Instance != null)
|
||||
{
|
||||
GlobalHotkeyConflictManager.Instance.ConflictsUpdated += OnConflictsUpdated;
|
||||
GlobalHotkeyConflictManager.Instance.RequestAllConflicts();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnConflictsUpdated(object sender, AllHotkeyConflictsEventArgs e)
|
||||
{
|
||||
this.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () =>
|
||||
{
|
||||
AllHotkeyConflictsData = e.Conflicts ?? new AllHotkeyConflictsData();
|
||||
});
|
||||
}
|
||||
|
||||
private void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
private void SettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||
@@ -80,6 +254,18 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
||||
ViewModel.LogOpeningSettingsEvent();
|
||||
}
|
||||
|
||||
private void ShortcutConflictBtn_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
if (AllHotkeyConflictsData == null || !HasConflicts)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Create and show the shortcut conflict window
|
||||
var conflictWindow = new ShortcutConflictWindow();
|
||||
conflictWindow.Activate();
|
||||
}
|
||||
|
||||
protected override void OnNavigatedTo(NavigationEventArgs e)
|
||||
{
|
||||
ViewModel.LogOpeningModuleEvent();
|
||||
@@ -88,6 +274,12 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||
{
|
||||
ViewModel.LogClosingModuleEvent();
|
||||
|
||||
// Unsubscribe from conflict updates when leaving the page
|
||||
if (GlobalHotkeyConflictManager.Instance != null)
|
||||
{
|
||||
GlobalHotkeyConflictManager.Instance.ConflictsUpdated -= OnConflictsUpdated;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,141 +11,177 @@
|
||||
Loaded="Page_Loaded"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<!-- Main layout container -->
|
||||
<Grid>
|
||||
<!-- Main content grid -->
|
||||
<Grid Margin="0,24,0,0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Margin="0,24,0,0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<!-- Compact Header overlay that covers both InfoBar and Title sections -->
|
||||
<Border
|
||||
x:Name="HeaderOverlay"
|
||||
Grid.Row="0"
|
||||
Grid.RowSpan="2"
|
||||
Margin="0,-24,0,0"
|
||||
VerticalAlignment="Top"
|
||||
BorderThickness="0"
|
||||
Canvas.ZIndex="1">
|
||||
|
||||
<tkcontrols:SettingsCard
|
||||
x:Name="WhatsNewDataDiagnosticsInfoBar"
|
||||
x:Uid="Oobe_WhatsNew_DataDiagnostics_InfoBar"
|
||||
Margin="0,-24,0,0"
|
||||
Background="{ThemeResource InfoBarInformationalSeverityBackgroundBrush}"
|
||||
IsTabStop="{x:Bind ShowDataDiagnosticsInfoBar, Mode=OneWay}"
|
||||
Visibility="{x:Bind ShowDataDiagnosticsInfoBar, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<tkcontrols:SettingsCard.HeaderIcon>
|
||||
<FontIcon Foreground="{ThemeResource InfoBarInformationalSeverityIconBackground}" Glyph="" />
|
||||
</tkcontrols:SettingsCard.HeaderIcon>
|
||||
<tkcontrols:SettingsCard.Description>
|
||||
<StackPanel>
|
||||
<TextBlock x:Name="WhatsNewDataDiagnosticsInfoBarDescText">
|
||||
<Hyperlink NavigateUri="https://aka.ms/powertoys-data-and-privacy-documentation">
|
||||
<Run x:Uid="Oobe_WhatsNew_DataDiagnostics_InfoBar_Desc" />
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
<TextBlock x:Name="WhatsNewDataDiagnosticsInfoBarDescTextYesClicked" Visibility="Collapsed">
|
||||
<Run x:Uid="Oobe_WhatsNew_DataDiagnostics_Yes_Click_InfoBar_Desc" />
|
||||
<Hyperlink Click="DataDiagnostics_OpenSettings_Click">
|
||||
<Run x:Uid="Oobe_WhatsNew_DataDiagnostics_Yes_Click_OpenSettings_Text" />
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</tkcontrols:SettingsCard.Description>
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Button
|
||||
x:Name="DataDiagnosticsButtonYes"
|
||||
x:Uid="Oobe_WhatsNew_DataDiagnostics_Button_Yes"
|
||||
Click="DataDiagnostics_InfoBar_YesNo_Click"
|
||||
CommandParameter="Yes" />
|
||||
<HyperlinkButton
|
||||
x:Name="DataDiagnosticsButtonNo"
|
||||
x:Uid="Oobe_WhatsNew_DataDiagnostics_Button_No"
|
||||
Click="DataDiagnostics_InfoBar_YesNo_Click"
|
||||
CommandParameter="No" />
|
||||
<Button
|
||||
Margin="16,0,0,0"
|
||||
Click="DataDiagnostics_InfoBar_Close_Click"
|
||||
Content="{ui:FontIcon Glyph=,
|
||||
FontSize=16}"
|
||||
Style="{StaticResource SubtleButtonStyle}" />
|
||||
</StackPanel>
|
||||
</tkcontrols:SettingsCard>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Margin="32,16,0,16"
|
||||
VerticalAlignment="Top"
|
||||
Orientation="Vertical">
|
||||
<TextBlock
|
||||
x:Uid="Oobe_WhatsNew"
|
||||
AutomationProperties.HeadingLevel="Level1"
|
||||
Style="{StaticResource TitleTextBlockStyle}" />
|
||||
<HyperlinkButton NavigateUri="https://github.com/microsoft/PowerToys/releases" Style="{StaticResource TextButtonStyle}">
|
||||
<TextBlock x:Uid="Oobe_WhatsNew_DetailedReleaseNotesLink" TextWrapping="Wrap" />
|
||||
</HyperlinkButton>
|
||||
</StackPanel>
|
||||
<tkcontrols:SettingsCard
|
||||
x:Name="WhatsNewDataDiagnosticsInfoBar"
|
||||
x:Uid="Oobe_WhatsNew_DataDiagnostics_InfoBar"
|
||||
Grid.Row="0"
|
||||
Padding="12,8,12,8"
|
||||
Background="{ThemeResource InfoBarInformationalSeverityBackgroundBrush}"
|
||||
IsTabStop="{x:Bind ShowDataDiagnosticsInfoBar, Mode=OneWay}"
|
||||
Visibility="{x:Bind ShowDataDiagnosticsInfoBar, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<tkcontrols:SettingsCard.HeaderIcon>
|
||||
<FontIcon Foreground="{ThemeResource InfoBarInformationalSeverityIconBackground}" Glyph="" />
|
||||
</tkcontrols:SettingsCard.HeaderIcon>
|
||||
<tkcontrols:SettingsCard.Description>
|
||||
<StackPanel>
|
||||
<TextBlock x:Name="WhatsNewDataDiagnosticsInfoBarDescText">
|
||||
<Hyperlink NavigateUri="https://aka.ms/powertoys-data-and-privacy-documentation">
|
||||
<Run x:Uid="Oobe_WhatsNew_DataDiagnostics_InfoBar_Desc" />
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
<TextBlock x:Name="WhatsNewDataDiagnosticsInfoBarDescTextYesClicked" Visibility="Collapsed">
|
||||
<Run x:Uid="Oobe_WhatsNew_DataDiagnostics_Yes_Click_InfoBar_Desc" />
|
||||
<Hyperlink Click="DataDiagnostics_OpenSettings_Click">
|
||||
<Run x:Uid="Oobe_WhatsNew_DataDiagnostics_Yes_Click_OpenSettings_Text" />
|
||||
</Hyperlink>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</tkcontrols:SettingsCard.Description>
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<Button
|
||||
x:Name="DataDiagnosticsButtonYes"
|
||||
x:Uid="Oobe_WhatsNew_DataDiagnostics_Button_Yes"
|
||||
Click="DataDiagnostics_InfoBar_YesNo_Click"
|
||||
CommandParameter="Yes" />
|
||||
<HyperlinkButton
|
||||
x:Name="DataDiagnosticsButtonNo"
|
||||
x:Uid="Oobe_WhatsNew_DataDiagnostics_Button_No"
|
||||
Click="DataDiagnostics_InfoBar_YesNo_Click"
|
||||
CommandParameter="No" />
|
||||
<Button
|
||||
Margin="16,0,0,0"
|
||||
Click="DataDiagnostics_InfoBar_Close_Click"
|
||||
Content="{ui:FontIcon Glyph=,
|
||||
FontSize=16}"
|
||||
Style="{StaticResource SubtleButtonStyle}" />
|
||||
</StackPanel>
|
||||
</tkcontrols:SettingsCard>
|
||||
|
||||
<InfoBar
|
||||
x:Name="ErrorInfoBar"
|
||||
x:Uid="Oobe_WhatsNew_LoadingError"
|
||||
Grid.Row="2"
|
||||
VerticalAlignment="Top"
|
||||
IsClosable="False"
|
||||
IsTabStop="False"
|
||||
Severity="Error">
|
||||
<InfoBar.ActionButton>
|
||||
<Button
|
||||
x:Uid="RetryBtn"
|
||||
HorizontalAlignment="Right"
|
||||
Click="LoadReleaseNotes_Click">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<FontIcon FontSize="16" Glyph="" />
|
||||
<TextBlock x:Uid="RetryLabel" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</InfoBar.ActionButton>
|
||||
</InfoBar>
|
||||
<InfoBar
|
||||
x:Name="ProxyWarningInfoBar"
|
||||
x:Uid="Oobe_WhatsNew_ProxyAuthenticationWarning"
|
||||
Grid.Row="2"
|
||||
VerticalAlignment="Top"
|
||||
IsClosable="False"
|
||||
IsTabStop="False"
|
||||
Severity="Warning">
|
||||
<InfoBar.ActionButton>
|
||||
<Button
|
||||
x:Uid="RetryBtn"
|
||||
HorizontalAlignment="Right"
|
||||
Click="LoadReleaseNotes_Click">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<FontIcon FontSize="16" Glyph="" />
|
||||
<TextBlock x:Uid="RetryLabel" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</InfoBar.ActionButton>
|
||||
</InfoBar>
|
||||
<Grid Grid.Row="1" Margin="16,12,0,12">
|
||||
<StackPanel
|
||||
VerticalAlignment="Top"
|
||||
Orientation="Vertical"
|
||||
Spacing="4">
|
||||
<TextBlock
|
||||
x:Uid="Oobe_WhatsNew"
|
||||
AutomationProperties.HeadingLevel="Level1"
|
||||
Style="{StaticResource TitleTextBlockStyle}" />
|
||||
<HyperlinkButton NavigateUri="https://github.com/microsoft/PowerToys/releases" Style="{StaticResource TextButtonStyle}">
|
||||
<TextBlock x:Uid="Oobe_WhatsNew_DetailedReleaseNotesLink" TextWrapping="Wrap" />
|
||||
</HyperlinkButton>
|
||||
</StackPanel>
|
||||
|
||||
<ScrollViewer Grid.Row="3" VerticalScrollBarVisibility="Auto">
|
||||
<Grid Margin="32,24,32,24">
|
||||
<ProgressRing
|
||||
x:Name="LoadingProgressRing"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
IsIndeterminate="True"
|
||||
Visibility="Visible" />
|
||||
<tk7controls:MarkdownTextBlock
|
||||
x:Name="ReleaseNotesMarkdown"
|
||||
VerticalAlignment="Top"
|
||||
Background="Transparent"
|
||||
Header1FontSize="20"
|
||||
Header1FontWeight="SemiBold"
|
||||
Header1Margin="0,0,0,4"
|
||||
Header3FontSize="16"
|
||||
Header3FontWeight="SemiBold"
|
||||
Header4FontSize="16"
|
||||
Header4FontWeight="SemiBold"
|
||||
HorizontalRuleMargin="24"
|
||||
LinkClicked="ReleaseNotesMarkdown_LinkClicked"
|
||||
ListMargin="-18,4,0,12"
|
||||
ParagraphMargin="0,0,0,0"
|
||||
TableMargin="24"
|
||||
Visibility="Collapsed" />
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
<!-- ShortcutConflictControl positioned at the right side -->
|
||||
<controls:ShortcutConflictControl
|
||||
Grid.RowSpan="2"
|
||||
Margin="0,0,16,0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
AllHotkeyConflictsData="{x:Bind AllHotkeyConflictsData, Mode=OneWay}"
|
||||
Visibility="{x:Bind HasConflicts, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- Reduced spacer for the compact header overlay -->
|
||||
<Grid Grid.Row="0" Height="0" />
|
||||
<Grid Grid.Row="1" Height="80" />
|
||||
|
||||
<InfoBar
|
||||
x:Name="ErrorInfoBar"
|
||||
x:Uid="Oobe_WhatsNew_LoadingError"
|
||||
Grid.Row="2"
|
||||
VerticalAlignment="Top"
|
||||
IsClosable="False"
|
||||
IsTabStop="False"
|
||||
Severity="Error">
|
||||
<InfoBar.ActionButton>
|
||||
<Button
|
||||
x:Uid="RetryBtn"
|
||||
HorizontalAlignment="Right"
|
||||
Click="LoadReleaseNotes_Click">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<FontIcon FontSize="16" Glyph="" />
|
||||
<TextBlock x:Uid="RetryLabel" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</InfoBar.ActionButton>
|
||||
</InfoBar>
|
||||
<InfoBar
|
||||
x:Name="ProxyWarningInfoBar"
|
||||
x:Uid="Oobe_WhatsNew_ProxyAuthenticationWarning"
|
||||
Grid.Row="2"
|
||||
VerticalAlignment="Top"
|
||||
IsClosable="False"
|
||||
IsTabStop="False"
|
||||
Severity="Warning">
|
||||
<InfoBar.ActionButton>
|
||||
<Button
|
||||
x:Uid="RetryBtn"
|
||||
HorizontalAlignment="Right"
|
||||
Click="LoadReleaseNotes_Click">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<FontIcon FontSize="16" Glyph="" />
|
||||
<TextBlock x:Uid="RetryLabel" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</InfoBar.ActionButton>
|
||||
</InfoBar>
|
||||
|
||||
<ScrollViewer Grid.Row="3" VerticalScrollBarVisibility="Auto">
|
||||
<Grid Margin="32,16,32,24">
|
||||
<ProgressRing
|
||||
x:Name="LoadingProgressRing"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
IsIndeterminate="True"
|
||||
Visibility="Visible" />
|
||||
<tk7controls:MarkdownTextBlock
|
||||
x:Name="ReleaseNotesMarkdown"
|
||||
VerticalAlignment="Top"
|
||||
Background="Transparent"
|
||||
Header1FontSize="20"
|
||||
Header1FontWeight="SemiBold"
|
||||
Header1Margin="0,0,0,4"
|
||||
Header3FontSize="16"
|
||||
Header3FontWeight="SemiBold"
|
||||
Header4FontSize="16"
|
||||
Header4FontWeight="SemiBold"
|
||||
HorizontalRuleMargin="24"
|
||||
LinkClicked="ReleaseNotesMarkdown_LinkClicked"
|
||||
ListMargin="-18,4,0,12"
|
||||
ParagraphMargin="0,0,0,0"
|
||||
TableMargin="24"
|
||||
Visibility="Collapsed" />
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Page>
|
||||
</Page>
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@@ -17,9 +18,11 @@ using CommunityToolkit.WinUI.UI.Controls;
|
||||
using global::PowerToys.GPOWrapper;
|
||||
using ManagedCommon;
|
||||
using Microsoft.PowerToys.Settings.UI.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.HotkeyConflicts;
|
||||
using Microsoft.PowerToys.Settings.UI.OOBE.Enums;
|
||||
using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel;
|
||||
using Microsoft.PowerToys.Settings.UI.SerializationContext;
|
||||
using Microsoft.PowerToys.Settings.UI.Services;
|
||||
using Microsoft.PowerToys.Settings.UI.Views;
|
||||
using Microsoft.PowerToys.Telemetry;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
@@ -27,12 +30,54 @@ using Microsoft.UI.Xaml.Navigation;
|
||||
|
||||
namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
||||
{
|
||||
public sealed partial class OobeWhatsNew : Page
|
||||
public sealed partial class OobeWhatsNew : Page, INotifyPropertyChanged
|
||||
{
|
||||
public OobePowerToysModule ViewModel { get; set; }
|
||||
|
||||
private AllHotkeyConflictsData _allHotkeyConflictsData = new AllHotkeyConflictsData();
|
||||
|
||||
public bool ShowDataDiagnosticsInfoBar => GetShowDataDiagnosticsInfoBar();
|
||||
|
||||
public AllHotkeyConflictsData AllHotkeyConflictsData
|
||||
{
|
||||
get => _allHotkeyConflictsData;
|
||||
set
|
||||
{
|
||||
if (_allHotkeyConflictsData != value)
|
||||
{
|
||||
_allHotkeyConflictsData = value;
|
||||
OnPropertyChanged(nameof(AllHotkeyConflictsData));
|
||||
OnPropertyChanged(nameof(HasConflicts));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasConflicts
|
||||
{
|
||||
get
|
||||
{
|
||||
if (AllHotkeyConflictsData == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
if (AllHotkeyConflictsData.InAppConflicts != null)
|
||||
{
|
||||
count += AllHotkeyConflictsData.InAppConflicts.Count;
|
||||
}
|
||||
|
||||
if (AllHotkeyConflictsData.SystemConflicts != null)
|
||||
{
|
||||
count += AllHotkeyConflictsData.SystemConflicts.Count;
|
||||
}
|
||||
|
||||
return count > 0;
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OobeWhatsNew"/> class.
|
||||
/// </summary>
|
||||
@@ -40,7 +85,27 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
||||
{
|
||||
this.InitializeComponent();
|
||||
ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.WhatsNew]);
|
||||
DataContext = ViewModel;
|
||||
DataContext = this;
|
||||
|
||||
// Subscribe to hotkey conflict updates
|
||||
if (GlobalHotkeyConflictManager.Instance != null)
|
||||
{
|
||||
GlobalHotkeyConflictManager.Instance.ConflictsUpdated += OnConflictsUpdated;
|
||||
GlobalHotkeyConflictManager.Instance.RequestAllConflicts();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnConflictsUpdated(object sender, AllHotkeyConflictsEventArgs e)
|
||||
{
|
||||
this.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () =>
|
||||
{
|
||||
AllHotkeyConflictsData = e.Conflicts ?? new AllHotkeyConflictsData();
|
||||
});
|
||||
}
|
||||
|
||||
private void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
private bool GetShowDataDiagnosticsInfoBar()
|
||||
@@ -184,6 +249,12 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views
|
||||
protected override void OnNavigatedFrom(NavigationEventArgs e)
|
||||
{
|
||||
ViewModel.LogClosingModuleEvent();
|
||||
|
||||
// Unsubscribe from conflict updates when leaving the page
|
||||
if (GlobalHotkeyConflictManager.Instance != null)
|
||||
{
|
||||
GlobalHotkeyConflictManager.Instance.ConflictsUpdated -= OnConflictsUpdated;
|
||||
}
|
||||
}
|
||||
|
||||
private void ReleaseNotesMarkdown_LinkClicked(object sender, LinkClickedEventArgs e)
|
||||
|
||||
Reference in New Issue
Block a user