[KBM]Launch apps / URI with keyboard shortcuts, support chords (#30121)

* Working UI update with just runProgram Path and isRunProgram

* First working, basic. no args or path, or setting change detections.

* Revert and fixed.

* Some clean up, working with config file monitor

* Args and Start-in should be working.

* File monitor, quotes, xaml screens one

* Fixed enable/disable toogle from XAML

* Code cleanup.

* Betting logging.

* Cleanup, start of RunProgramDescriptor and usage of run_non_elevated/run_elevated

* Code moved to KeyboardEventHandlers, but not enabled since it won't build as is, needs elevation.h. Other testing..

* Key chords working, pretty much

* Added gui for elevation level, need to refresh on change...

* f: include shellapi.h and reference wil in KBMEL

* run_elevated/run_non_elevated sorted out. Working!

* Removed lots of old temp code.

* Fix some speling errors.

* Cleanup before trying to add a UI for the chord

* Added "DifferentUser" option

* Closer on UI for chords.

* Better UI, lots working.

* Clean up

* Text for “Allow chords” – needs to look better…

* Bugs and clean-up

* Cleanup

* Refactor and clean up.

* More clean up

* Some localization.

* Don’t show “Allow chords“ to the “to” shortcut

* Maybe better foreground after opening new app

* Better chord matching.

* Runprogram fix for stealing existing shortcut.

* Better runProgram stuff

* Temp commit

* Working well

* Toast test

* More toast

* Added File and Folder picker UI

* Pre-check on run program file exists.

* Refactor to SetupRunProgramControls

* Open URI UI is going.

* Open URI working well

* Open URI stuff working well

* Allowed AppSpecific shortcut and fixed backup/restore shortcut dups

* Fixed settings screen

* Start of code to find by name...

* UI fixed

* Small fixes

* Some single edit code working.

* UI getting better.

* Fixes

* Fixed and merge from main

* UI updates

* UI updates.

* UI stuff

* Fixed crash from move ui item locations.

* Fixed crash from move ui item locations.

* Added delete confirm

* Basic sound working.

* Localized some stuff

* Added sounds

* Better experiance when shortcut is in use.

* UI tweaks

* Fixed KBM ui for unicode shortcut not having ","

* Some clean up

* Cleanup

* Cleanup

* Fixed applyXamlStyling

* Added back stuff lost in merge

* applyXamlStyling, again

* Fixed crash on change from non shortcut to shortcut

* Update src/modules/keyboardmanager/KeyboardManagerEngineTest/KeyboardManagerEngineTest.vcxproj

* Fixed some spelling type issues.

* ImplementationLibrary 231216

* Comment bump to see if the Microsoft.Windows.ImplementationLibrary version thing gets picked up

* Correct, Microsoft.Windows.ImplementationLibrary, finally?

* Fixed two test that failed because we now allow key-chords.

* Removed shortcut sounds.

* use original behavior when "allow chords" is off in shortcut window

* fix crash when editing a shortcut that has apps specified for it

* split KBM chords with comma on dashboard page

* Fix some spelling items.

* More "spelling"

* Fix XAML styling

* align TextBlock and ToggleSwitch

* fix cutoff issue at the top

* increase ComboBox width

* Added *Unsupported* for backwards compat on config of KBM

* fix spellcheck

* Fix crash on Remap key screen

* Fixed Remap Keys ComboBox width too short.

* Removed KBM Single Edit mode, fixed crash.

* Fix Xaml with xaml cops

* Fix crash on setting "target app" for some types of shortcuts.

* Space to toggle chord, combobox back

* fix spellcheck

* fix some code nits

* Code review updates.

* Add exclusions to the bug report tool

* Code review and kill CloseAndEndTask

* Fix alignment / 3 comboboxes per row

* Fix daily telemetry events to exclude start app and open URI

* Add chords and remove app start and open uri from config telemetry

* comma instead of plus in human readable shortcut telemetry data

* Code review, restore default-old state when new row added in KBM

* Code review, restore default-old state when new row added in KBM, part 2

* Still show target app on Settings

* Only allow enabling chords for origin shortcuts

---------

Co-authored-by: Andrey Nekrasov <yuyoyuppe@users.noreply.github.com>
Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
This commit is contained in:
Jeff Lord
2024-02-27 18:12:05 -05:00
committed by GitHub
parent 1a5349bf1e
commit 725c8e8c19
48 changed files with 4373 additions and 1571 deletions

View File

@@ -18,9 +18,29 @@ namespace Microsoft.PowerToys.Settings.UI.Library
return base.GetMappedOriginalKeys();
}
public new List<string> GetMappedNewRemapKeys()
public new List<string> GetMappedOriginalKeysWithSplitChord()
{
return base.GetMappedNewRemapKeys();
return base.GetMappedOriginalKeysWithSplitChord();
}
public List<string> GetMappedOriginalKeys(bool ignoreSecondKeyInChord)
{
return base.GetMappedOriginalKeys(ignoreSecondKeyInChord);
}
public List<string> GetMappedOriginalKeysWithoutChord()
{
return base.GetMappedOriginalKeys(true);
}
public new List<string> GetMappedOriginalKeysOnlyChord()
{
return base.GetMappedOriginalKeysOnlyChord();
}
public new List<string> GetMappedNewRemapKeys(int runProgramMaxLength)
{
return base.GetMappedNewRemapKeys(runProgramMaxLength);
}
public bool Compare(AppSpecificKeysDataModel arg)

View File

@@ -2,42 +2,312 @@
// 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.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Management;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Windows.Input;
using ManagedCommon;
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
using Microsoft.PowerToys.Settings.UI.Library.ViewModels.Commands;
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class KeysDataModel
public class KeysDataModel : INotifyPropertyChanged
{
[JsonPropertyName("originalKeys")]
public string OriginalKeys { get; set; }
[JsonPropertyName("secondKeyOfChord")]
public int SecondKeyOfChord { get; set; }
[JsonPropertyName("newRemapKeys")]
public string NewRemapKeys { get; set; }
[JsonPropertyName("unicodeText")]
public string NewRemapString { get; set; }
[JsonPropertyName("runProgramFilePath")]
public string RunProgramFilePath { get; set; }
[JsonPropertyName("runProgramArgs")]
public string RunProgramArgs { get; set; }
[JsonPropertyName("openUri")]
public string OpenUri { get; set; }
[JsonPropertyName("operationType")]
public int OperationType { get; set; }
private enum KeyboardManagerEditorType
{
KeyEditor = 0,
ShortcutEditor,
}
public const string CommaSeparator = "<comma>";
private static Process editor;
private ICommand _editShortcutItemCommand;
public ICommand EditShortcutItem => _editShortcutItemCommand ?? (_editShortcutItemCommand = new RelayCommand<object>(OnEditShortcutItem));
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void OnEditShortcutItem(object parameter)
{
OpenEditor((int)KeyboardManagerEditorType.ShortcutEditor);
}
private async void OpenEditor(int type)
{
if (editor != null)
{
BringProcessToFront(editor);
return;
}
const string PowerToyName = KeyboardManagerSettings.ModuleName;
const string KeyboardManagerEditorPath = "KeyboardManagerEditor\\PowerToys.KeyboardManagerEditor.exe";
try
{
if (editor != null && editor.HasExited)
{
Logger.LogInfo($"Previous instance of {PowerToyName} editor exited");
editor = null;
}
if (editor != null)
{
Logger.LogInfo($"The {PowerToyName} editor instance {editor.Id} exists. Bringing the process to the front");
BringProcessToFront(editor);
return;
}
string path = Path.Combine(Environment.CurrentDirectory, KeyboardManagerEditorPath);
Logger.LogInfo($"Starting {PowerToyName} editor from {path}");
// InvariantCulture: type represents the KeyboardManagerEditorType enum value
editor = Process.Start(path, $"{type.ToString(CultureInfo.InvariantCulture)} {Environment.ProcessId}");
await editor.WaitForExitAsync();
editor = null;
}
catch (Exception e)
{
editor = null;
Logger.LogError($"Exception encountered when opening an {PowerToyName} editor", e);
}
}
private static void BringProcessToFront(Process process)
{
if (process == null)
{
return;
}
IntPtr handle = process.MainWindowHandle;
if (NativeMethods.IsIconic(handle))
{
NativeMethods.ShowWindow(handle, NativeMethods.SWRESTORE);
}
NativeMethods.SetForegroundWindow(handle);
}
private static List<string> MapKeysOnlyChord(int secondKeyOfChord)
{
var result = new List<string>();
if (secondKeyOfChord <= 0)
{
return result;
}
result.Add(Helper.GetKeyName((uint)secondKeyOfChord));
return result;
}
private static List<string> MapKeys(string stringOfKeys, int secondKeyOfChord, bool splitChordsWithComma = false)
{
if (stringOfKeys == null)
{
return new List<string>();
}
if (secondKeyOfChord > 0)
{
var keys = stringOfKeys.Split(';');
return keys.Take(keys.Length - 1)
.Select(uint.Parse)
.Select(Helper.GetKeyName)
.ToList();
}
else
{
if (splitChordsWithComma)
{
var keys = stringOfKeys.Split(';')
.Select(uint.Parse)
.Select(Helper.GetKeyName)
.ToList();
keys.Insert(keys.Count - 1, CommaSeparator);
return keys;
}
else
{
return stringOfKeys
.Split(';')
.Select(uint.Parse)
.Select(Helper.GetKeyName)
.ToList();
}
}
}
private static List<string> MapKeys(string stringOfKeys)
{
return stringOfKeys
.Split(';')
.Select(uint.Parse)
.Select(Helper.GetKeyName)
.ToList();
return MapKeys(stringOfKeys, 0);
}
public List<string> GetMappedOriginalKeys(bool ignoreSecondKeyInChord, bool splitChordsWithComma = false)
{
if (ignoreSecondKeyInChord && SecondKeyOfChord > 0)
{
return MapKeys(OriginalKeys, SecondKeyOfChord);
}
else
{
return MapKeys(OriginalKeys, -1, splitChordsWithComma);
}
}
public List<string> GetMappedOriginalKeysOnlyChord()
{
return MapKeysOnlyChord(SecondKeyOfChord);
}
public List<string> GetMappedOriginalKeys()
{
return MapKeys(OriginalKeys);
return GetMappedOriginalKeys(false);
}
public List<string> GetMappedNewRemapKeys()
public List<string> GetMappedOriginalKeysWithSplitChord()
{
return string.IsNullOrEmpty(NewRemapString) ? MapKeys(NewRemapKeys) : new List<string> { NewRemapString };
return GetMappedOriginalKeys(false, true);
}
public bool IsRunProgram
{
get
{
return OperationType == 1;
}
}
public bool IsOpenURI
{
get
{
return OperationType == 2;
}
}
public bool IsOpenUriOrIsRunProgram
{
get
{
return IsOpenURI || IsRunProgram;
}
}
public bool HasChord
{
get
{
return SecondKeyOfChord > 0;
}
}
public List<string> GetMappedNewRemapKeys(int runProgramMaxLength)
{
if (IsRunProgram)
{
// we're going to just pretend this is a "key" if we have a RunProgramFilePath
if (string.IsNullOrEmpty(RunProgramFilePath))
{
return new List<string>();
}
else
{
return new List<string> { FormatFakeKeyForDisplay(runProgramMaxLength) };
}
}
else if (IsOpenURI)
{
// we're going to just pretend this is a "key" if we have a RunProgramFilePath
if (string.IsNullOrEmpty(OpenUri))
{
return new List<string>();
}
else
{
if (OpenUri.Length > runProgramMaxLength)
{
return new List<string> { $"{OpenUri.Substring(0, runProgramMaxLength - 3)}..." };
}
else
{
return new List<string> { OpenUri };
}
}
}
return (string.IsNullOrEmpty(NewRemapString) || NewRemapString == "*Unsupported*") ? MapKeys(NewRemapKeys) : new List<string> { NewRemapString };
}
// Instead of doing something fancy pants, we 'll just display the RunProgramFilePath data when it's IsRunProgram
// It truncates the start of the program to run, if it's long and truncates the end of the args if it's long
// e.g.: c:\MyCool\PathIs\Long\software.exe myArg1 myArg2 myArg3 -> (something like) "...ng\software.exe myArg1..."
// the idea is you get the most important part of the program to run and some of the args in case that the only thing thats different,
// e.g: "...path\software.exe cool1.txt" and "...path\software.exe cool3.txt"
private string FormatFakeKeyForDisplay(int runProgramMaxLength)
{
// was going to use this:
var fakeKey = Environment.ExpandEnvironmentVariables(RunProgramFilePath);
try
{
if (File.Exists(fakeKey))
{
fakeKey = Path.GetFileName(Environment.ExpandEnvironmentVariables(RunProgramFilePath));
}
}
catch
{
}
fakeKey = $"{fakeKey} {RunProgramArgs}".Trim();
if (fakeKey.Length > runProgramMaxLength)
{
fakeKey = $"{fakeKey.Substring(0, runProgramMaxLength - 3)}...";
}
return fakeKey;
}
public string ToJsonString()

View File

@@ -324,8 +324,24 @@ namespace Microsoft.PowerToys.Settings.UI.Library
// the settings file needs to be updated, update the real one with non-excluded stuff...
Logger.LogInfo($"Settings file {currentFile.Key} is different and is getting updated from backup");
var newCurrentSettingsFile = JsonMergeHelper.Merge(File.ReadAllText(currentSettingsFiles[currentFile.Key]), settingsToRestoreJson);
File.WriteAllText(currentSettingsFiles[currentFile.Key], newCurrentSettingsFile);
// we needed a new "CustomRestoreSettings" for now, to overwrite because some settings don't merge well (like KBM shortcuts)
var overwrite = false;
if (backupRestoreSettings["CustomRestoreSettings"] != null && backupRestoreSettings["CustomRestoreSettings"][currentFile.Key] != null)
{
var customRestoreSettings = backupRestoreSettings["CustomRestoreSettings"][currentFile.Key];
overwrite = customRestoreSettings["overwrite"] != null && (bool)customRestoreSettings["overwrite"];
}
if (overwrite)
{
File.WriteAllText(currentSettingsFiles[currentFile.Key], settingsToRestoreJson);
}
else
{
var newCurrentSettingsFile = JsonMergeHelper.Merge(File.ReadAllText(currentSettingsFiles[currentFile.Key]), settingsToRestoreJson);
File.WriteAllText(currentSettingsFiles[currentFile.Key], newCurrentSettingsFile);
}
anyFilesUpdated = true;
}
}

View File

@@ -26,6 +26,11 @@
]
}
],
"CustomRestoreSettings": {
"\\Keyboard Manager\\default.json": {
"overwrite": true
}
},
"IgnoredSettings": {
"backup-restore_settings.json": [
"RestartAfterRestore"

View File

@@ -0,0 +1,23 @@
// 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 Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace Microsoft.PowerToys.Settings.UI.Converters
{
internal sealed class KeyVisualTemplateSelector : DataTemplateSelector
{
public DataTemplate KeyVisualTemplate { get; set; }
public DataTemplate CommaTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
var stringValue = item as string;
return stringValue == KeysDataModel.CommaSeparator ? CommaTemplate : KeyVisualTemplate;
}
}
}

View File

@@ -15,6 +15,26 @@
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="KeyVisualTemplate">
<controls:KeyVisual
VerticalAlignment="Center"
AutomationProperties.AccessibilityView="Raw"
Content="{Binding}"
IsTabStop="False"
VisualType="TextOnly" />
</DataTemplate>
<DataTemplate x:Key="CommaTemplate">
<StackPanel Background="{ThemeResource SystemFillColorSolidAttentionBackground}">
<TextBlock
Margin="4,0"
VerticalAlignment="Bottom"
Text="," />
</StackPanel>
</DataTemplate>
<converters:KeyVisualTemplateSelector
x:Key="KeyVisualTemplateSelector"
CommaTemplate="{StaticResource CommaTemplate}"
KeyVisualTemplate="{StaticResource KeyVisualTemplate}" />
<converters:ModuleItemTemplateSelector
x:Key="ModuleItemTemplateSelector"
ButtonTemplate="{StaticResource ModuleItemButtonTemplate}"
@@ -156,7 +176,7 @@
<ItemsControl
AutomationProperties.AccessibilityView="Raw"
IsTabStop="False"
ItemsSource="{x:Bind GetMappedNewRemapKeys()}">
ItemsSource="{x:Bind GetMappedNewRemapKeys(15)}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Spacing="12" />
@@ -190,7 +210,7 @@
<DataTemplate x:DataType="Lib:AppSpecificKeysDataModel">
<StackPanel Orientation="Horizontal">
<Border
Padding="8,4"
Padding="8,0"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
BorderThickness="1"
@@ -198,29 +218,27 @@
<ItemsControl
AutomationProperties.AccessibilityView="Raw"
IsTabStop="False"
ItemsSource="{x:Bind GetMappedOriginalKeys()}">
ItemTemplateSelector="{StaticResource KeyVisualTemplateSelector}"
ItemsSource="{x:Bind GetMappedOriginalKeysWithSplitChord()}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Spacing="12" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:KeyVisual
VerticalAlignment="Center"
AutomationProperties.AccessibilityView="Raw"
Content="{Binding}"
IsTabStop="False"
VisualType="TextOnly" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
<controls:IsEnabledTextBlock
x:Uid="To"
Margin="8,0,8,0"
VerticalAlignment="Center"
Style="{StaticResource SecondaryIsEnabledTextBlockStyle}" />
Style="{StaticResource SecondaryIsEnabledTextBlockStyle}"
Visibility="{x:Bind Path=IsOpenUriOrIsRunProgram, Mode=OneWay, Converter={StaticResource BoolToInvertedVisibilityConverter}}" />
<controls:IsEnabledTextBlock
x:Uid="Starts"
Margin="8,0,8,0"
VerticalAlignment="Center"
Style="{StaticResource SecondaryIsEnabledTextBlockStyle}"
Visibility="{x:Bind Path=IsOpenUriOrIsRunProgram, Mode=OneWay}" />
<Border
Padding="8,4"
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
@@ -230,7 +248,7 @@
<ItemsControl
AutomationProperties.AccessibilityView="Raw"
IsTabStop="False"
ItemsSource="{x:Bind GetMappedNewRemapKeys()}">
ItemsSource="{x:Bind GetMappedNewRemapKeys(15)}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Spacing="12" />

View File

@@ -14,6 +14,12 @@
<Page.Resources>
<tkconverters:CollectionVisibilityConverter x:Key="CollectionVisibilityConverter" />
<tkconverters:BoolToVisibilityConverter
x:Key="BoolToInvertedVisibilityConverter"
FalseValue="Visible"
TrueValue="Collapsed" />
<tkconverters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<Style x:Name="KeysListViewContainerStyle" TargetType="ListViewItem">
<Setter Property="IsTabStop" Value="False" />
</Style>
@@ -105,7 +111,7 @@
x:Uid="KeyboardManager_RemappedTo"
IsTabStop="False"
ItemTemplate="{StaticResource RemappedKeyTemplate}"
ItemsSource="{x:Bind GetMappedNewRemapKeys()}">
ItemsSource="{x:Bind GetMappedNewRemapKeys(50)}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Spacing="4" />
@@ -124,7 +130,7 @@
x:Uid="KeyboardManager_RemapShortcutsButton"
ActionIcon="{ui:FontIcon Glyph=&#xE8A7;}"
Command="{Binding Path=EditShortcutCommand}"
HeaderIcon="{ui:FontIcon Glyph=&#xE92E;}"
HeaderIcon="{ui:FontIcon Glyph=&#xE713;}"
IsClickEnabled="True" />
<ListView
@@ -136,47 +142,79 @@
Visibility="{x:Bind Path=ViewModel.RemapShortcuts, Mode=OneWay, Converter={StaticResource CollectionVisibilityConverter}}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="Lib:AppSpecificKeysDataModel">
<tkcontrols:SettingsCard ContentAlignment="Left">
<StackPanel Orientation="Horizontal">
<ItemsControl
IsTabStop="False"
ItemTemplate="{StaticResource OriginalKeyTemplate}"
ItemsSource="{x:Bind GetMappedOriginalKeys()}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Spacing="4" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<controls:IsEnabledTextBlock
x:Uid="To"
Margin="8,0,8,0"
VerticalAlignment="Center"
Style="{StaticResource SecondaryIsEnabledTextBlockStyle}" />
<ItemsControl
Name="KeyboardManager_RemappedTo"
x:Uid="KeyboardManager_RemappedTo"
IsTabStop="False"
ItemTemplate="{StaticResource RemappedKeyTemplate}"
ItemsSource="{x:Bind GetMappedNewRemapKeys()}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Spacing="4" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<Border
Margin="16,0,0,0"
Padding="12,4,12,6"
VerticalAlignment="Center"
CornerRadius="12">
<Border.Background>
<SolidColorBrush Opacity="0.3" Color="{ThemeResource SystemAccentColor}" />
</Border.Background>
<TextBlock Text="{x:Bind TargetApp}" />
</Border>
</StackPanel>
<tkcontrols:SettingsCard>
<tkcontrols:SettingsCard.Description>
<StackPanel Orientation="Horizontal">
<ItemsControl
IsTabStop="False"
ItemTemplate="{StaticResource OriginalKeyTemplate}"
ItemsSource="{x:Bind GetMappedOriginalKeysWithoutChord()}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Spacing="4" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<TextBlock
Padding="6,0,6,6"
VerticalAlignment="Bottom"
Text=","
Visibility="{x:Bind Path=HasChord, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}" />
<ItemsControl
IsTabStop="False"
ItemTemplate="{StaticResource OriginalKeyTemplate}"
ItemsSource="{x:Bind GetMappedOriginalKeysOnlyChord()}"
Visibility="{x:Bind Path=HasChord, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Spacing="4" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<controls:IsEnabledTextBlock
x:Uid="To"
Margin="8,0,8,0"
VerticalAlignment="Center"
Style="{StaticResource SecondaryIsEnabledTextBlockStyle}"
Visibility="{x:Bind Path=IsOpenUriOrIsRunProgram, Mode=OneWay, Converter={StaticResource BoolToInvertedVisibilityConverter}}" />
<controls:IsEnabledTextBlock
x:Uid="Starts"
Margin="8,0,8,0"
VerticalAlignment="Center"
Style="{StaticResource SecondaryIsEnabledTextBlockStyle}"
Visibility="{x:Bind Path=IsOpenUriOrIsRunProgram, Mode=OneWay}" />
<ItemsControl
Name="KeyboardManager_RemappedTo"
x:Uid="KeyboardManager_RemappedTo"
IsTabStop="False"
ItemTemplate="{StaticResource RemappedKeyTemplate}"
ItemsSource="{x:Bind GetMappedNewRemapKeys(50)}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Spacing="4" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<Border
Margin="16,0,0,0"
Padding="12,4,12,6"
VerticalAlignment="Center"
CornerRadius="12">
<Border.Background>
<SolidColorBrush Opacity="0.3" Color="{ThemeResource SystemAccentColor}" />
</Border.Background>
<TextBlock Text="{x:Bind TargetApp}" />
</Border>
</StackPanel>
</tkcontrols:SettingsCard.Description>
</tkcontrols:SettingsCard>
</DataTemplate>
</ListView.ItemTemplate>

View File

@@ -2367,6 +2367,10 @@ From there, simply click on one of the supported files in the File Explorer and
<value>to</value>
<comment>as in: from x to y</comment>
</data>
<data name="Starts.Text" xml:space="preserve">
<value>starts</value>
<comment>as in: doing x will start y</comment>
</data>
<data name="LearnMore_Awake.Text" xml:space="preserve">
<value>Learn more about Awake</value>
<comment>Awake is a product name, do not loc</comment>

View File

@@ -190,11 +190,11 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
}
else if (appSpecificShortcutList == null)
{
return globalShortcutList.ConvertAll(x => new AppSpecificKeysDataModel { OriginalKeys = x.OriginalKeys, NewRemapKeys = x.NewRemapKeys, NewRemapString = x.NewRemapString, TargetApp = allAppsDescription }).ToList();
return globalShortcutList.ConvertAll(x => new AppSpecificKeysDataModel { OriginalKeys = x.OriginalKeys, NewRemapKeys = x.NewRemapKeys, NewRemapString = x.NewRemapString, RunProgramFilePath = x.RunProgramFilePath, OperationType = x.OperationType, OpenUri = x.OpenUri, SecondKeyOfChord = x.SecondKeyOfChord, RunProgramArgs = x.RunProgramArgs, TargetApp = allAppsDescription }).ToList();
}
else
{
return globalShortcutList.ConvertAll(x => new AppSpecificKeysDataModel { OriginalKeys = x.OriginalKeys, NewRemapKeys = x.NewRemapKeys, NewRemapString = x.NewRemapString, TargetApp = allAppsDescription }).Concat(appSpecificShortcutList).ToList();
return globalShortcutList.ConvertAll(x => new AppSpecificKeysDataModel { OriginalKeys = x.OriginalKeys, NewRemapKeys = x.NewRemapKeys, NewRemapString = x.NewRemapString, RunProgramFilePath = x.RunProgramFilePath, OperationType = x.OperationType, OpenUri = x.OpenUri, SecondKeyOfChord = x.SecondKeyOfChord, RunProgramArgs = x.RunProgramArgs, TargetApp = allAppsDescription }).Concat(appSpecificShortcutList).ToList();
}
}