ignore holtkey conflict (#41729)

<!-- Enter a brief description/summary of your PR here. What does it
fix/what does it change/how was it tested (even manually, if necessary)?
-->
## Summary of the Pull Request
This PR implements functionality to ignore specific hotkey conflicts in
PowerToys settings. The primary purpose is to allow users to suppress
individual shortcut conflict warnings if they find their configurations
work correctly despite the detected conflicts.


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

- [x] Closes: #41544
- [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
- [x] **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
- Added hotkey conflict ignore functionality with user-controllable
settings
- Updated shortcut control UI to support ignore states and clearer
conflict messaging
- Enhanced conflict detection to respect ignored shortcuts when counting
conflicts

<!-- 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 <shuaiyuan@microsoft.com>
Signed-off-by: Shuai Yuan <shuai.yuan.zju@gmail.com>
Signed-off-by: Shawn Yuan (from Dev Box) <shuaiyuan@microsoft.com>
Co-authored-by: Niels Laute <niels.laute@live.nl>
This commit is contained in:
Shawn Yuan
2025-09-29 08:53:07 +08:00
committed by GitHub
parent b026bf5be2
commit 8d4ed04f1a
28 changed files with 1203 additions and 274 deletions

View File

@@ -128,14 +128,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
if (value != _hotkey)
{
if (value == null || value.IsEmpty())
{
_hotkey = AlwaysOnTopProperties.DefaultHotkeyValue;
}
else
{
_hotkey = value;
}
_hotkey = value ?? AlwaysOnTopProperties.DefaultHotkeyValue;
Settings.Properties.Hotkey.Value = _hotkey;
NotifyPropertyChanged();

View File

@@ -29,7 +29,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
protected override string ModuleName => "Dashboard";
private const string JsonFileType = ".json";
private Dispatcher dispatcher;
public Func<string, int> SendConfigMSG { get; }
@@ -88,6 +87,21 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
dispatcher.BeginInvoke(() =>
{
var allConflictData = e.Conflicts;
foreach (var inAppConflict in allConflictData.InAppConflicts)
{
var hotkey = inAppConflict.Hotkey;
var hotkeySetting = new HotkeySettings(hotkey.Win, hotkey.Ctrl, hotkey.Alt, hotkey.Shift, hotkey.Key);
inAppConflict.ConflictIgnored = HotkeyConflictIgnoreHelper.IsIgnoringConflicts(hotkeySetting);
}
foreach (var systemConflict in allConflictData.SystemConflicts)
{
var hotkey = systemConflict.Hotkey;
var hotkeySetting = new HotkeySettings(hotkey.Win, hotkey.Ctrl, hotkey.Alt, hotkey.Shift, hotkey.Key);
systemConflict.ConflictIgnored = HotkeyConflictIgnoreHelper.IsIgnoringConflicts(hotkeySetting);
}
AllHotkeyConflictsData = e.Conflicts ?? new AllHotkeyConflictsData();
});
}

View File

@@ -776,7 +776,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
if (value != _editorHotkey)
{
if (value == null || value.IsEmpty())
if (value == null)
{
_editorHotkey = FZConfigProperties.DefaultEditorHotkeyValue;
}
@@ -822,7 +822,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
if (value != _nextTabHotkey)
{
if (value == null || value.IsEmpty())
if (value == null)
{
_nextTabHotkey = FZConfigProperties.DefaultNextTabHotkeyValue;
}
@@ -848,7 +848,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
if (value != _prevTabHotkey)
{
if (value == null || value.IsEmpty())
if (value == null)
{
_prevTabHotkey = FZConfigProperties.DefaultPrevTabHotkeyValue;
}

View File

@@ -12,12 +12,10 @@ using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using System.Windows;
using System.Windows.Threading;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Helpers;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
using Microsoft.PowerToys.Settings.UI.Library.HotkeyConflicts;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
using Microsoft.PowerToys.Settings.UI.SerializationContext;
@@ -70,6 +68,36 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
protected override string ModuleName => "ShortcutConflictsWindow";
/// <summary>
/// Ignore a specific HotkeySettings
/// </summary>
/// <param name="hotkeySettings">The HotkeySettings to ignore</param>
public void IgnoreShortcut(HotkeySettings hotkeySettings)
{
if (hotkeySettings == null)
{
return;
}
HotkeyConflictIgnoreHelper.AddToIgnoredList(hotkeySettings);
GlobalHotkeyConflictManager.Instance?.RequestAllConflicts();
}
/// <summary>
/// Remove a HotkeySettings from the ignored list
/// </summary>
/// <param name="hotkeySettings">The HotkeySettings to unignore</param>
public void UnignoreShortcut(HotkeySettings hotkeySettings)
{
if (hotkeySettings == null)
{
return;
}
HotkeyConflictIgnoreHelper.RemoveFromIgnoredList(hotkeySettings);
GlobalHotkeyConflictManager.Instance?.RequestAllConflicts();
}
private IHotkeyConfig GetModuleSettings(string moduleKey)
{
try
@@ -120,20 +148,24 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
foreach (var conflict in conflicts)
{
ProcessConflictGroup(conflict, isSystemConflict);
HotkeySettings hotkey = new(conflict.Hotkey.Win, conflict.Hotkey.Ctrl, conflict.Hotkey.Alt, conflict.Hotkey.Shift, conflict.Hotkey.Key);
var isIgnored = HotkeyConflictIgnoreHelper.IsIgnoringConflicts(hotkey);
conflict.ConflictIgnored = isIgnored;
ProcessConflictGroup(conflict, isSystemConflict, isIgnored);
items.Add(conflict);
}
}
private void ProcessConflictGroup(HotkeyConflictGroupData conflict, bool isSystemConflict)
private void ProcessConflictGroup(HotkeyConflictGroupData conflict, bool isSystemConflict, bool isIgnored)
{
foreach (var module in conflict.Modules)
{
SetupModuleData(module, isSystemConflict);
SetupModuleData(module, isSystemConflict, isIgnored);
}
}
private void SetupModuleData(ModuleHotkeyData module, bool isSystemConflict)
private void SetupModuleData(ModuleHotkeyData module, bool isSystemConflict, bool isIgnored)
{
try
{
@@ -220,55 +252,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
}
}
private void SaveModuleSettingsAndNotify(string moduleName)
{
try
{
var settings = GetModuleSettings(moduleName);
if (settings is ISettingsConfig settingsConfig)
{
// No need to save settings here, the runner will call module interface to save it
// SaveSettingsToFile(settings);
// Send IPC notification using the same format as other ViewModels
SendConfigMSG(settingsConfig, moduleName);
System.Diagnostics.Debug.WriteLine($"Saved settings and sent IPC notification for module: {moduleName}");
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Error saving settings and notifying for {moduleName}: {ex.Message}");
}
}
private void SaveSettingsToFile(IHotkeyConfig settings)
{
try
{
// Get the repository for this settings type using reflection
var settingsType = settings.GetType();
var repositoryMethod = typeof(SettingsFactory).GetMethod("GetRepository");
if (repositoryMethod != null)
{
var genericMethod = repositoryMethod.MakeGenericMethod(settingsType);
var repository = genericMethod.Invoke(_settingsFactory, null);
if (repository != null)
{
var saveMethod = repository.GetType().GetMethod("SaveSettingsToFile");
saveMethod?.Invoke(repository, null);
System.Diagnostics.Debug.WriteLine($"Saved settings to file for type: {settingsType.Name}");
}
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Error saving settings to file: {ex.Message}");
}
}
/// <summary>
/// Sends IPC notification using the same format as other ViewModels
/// </summary>

View File

@@ -127,7 +127,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
if (value != _hotkey)
{
if (value == null || value.IsEmpty())
if (value == null)
{
_hotkey = WorkspacesProperties.DefaultHotkeyValue;
}