mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-02-23 19:49:43 +01:00
Enabling the edit dialog
This commit is contained in:
@@ -0,0 +1,314 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="KeyboardManagerEditorUI.Controls.UnifiedMappingControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:CommunityToolkit.WinUI.UI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:KeyboardManagerEditorUI.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
|
||||
Loaded="UserControl_Loaded"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="280" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Left Column: Original Input (Trigger) -->
|
||||
<StackPanel
|
||||
Grid.Column="0"
|
||||
Orientation="Vertical"
|
||||
Spacing="8">
|
||||
<TextBlock
|
||||
x:Uid="UnifiedMappingControlTriggerTextBlock"
|
||||
FontWeight="SemiBold"
|
||||
Text="Trigger" />
|
||||
|
||||
<ComboBox
|
||||
x:Name="TriggerTypeComboBox"
|
||||
HorizontalAlignment="Stretch"
|
||||
SelectionChanged="TriggerTypeComboBox_SelectionChanged">
|
||||
<ComboBoxItem Content="Key or shortcut" Tag="KeyOrShortcut" IsSelected="True" />
|
||||
<ComboBoxItem Content="Mouse button" Tag="Mouse" />
|
||||
</ComboBox>
|
||||
|
||||
<tkcontrols:SwitchPresenter
|
||||
x:Name="TriggerSwitchPresenter"
|
||||
TargetType="x:String"
|
||||
Value="{Binding SelectedItem.Tag, ElementName=TriggerTypeComboBox}">
|
||||
|
||||
<!-- Key or Shortcut Trigger -->
|
||||
<tkcontrols:Case Value="KeyOrShortcut">
|
||||
<ToggleButton
|
||||
x:Name="TriggerKeyToggleBtn"
|
||||
MinHeight="86"
|
||||
Padding="8,24,8,24"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Center"
|
||||
VerticalContentAlignment="Center"
|
||||
Checked="TriggerKeyToggleBtn_Checked"
|
||||
Style="{StaticResource CustomShortcutToggleButtonStyle}"
|
||||
Unchecked="TriggerKeyToggleBtn_Unchecked">
|
||||
<ToggleButton.Content>
|
||||
<ItemsControl x:Name="TriggerKeys">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<controls:WrapPanel
|
||||
HorizontalSpacing="4"
|
||||
Orientation="Horizontal"
|
||||
VerticalSpacing="4" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<local:KeyVisual Content="{Binding}" Style="{StaticResource DefaultKeyVisualStyle}" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ToggleButton.Content>
|
||||
</ToggleButton>
|
||||
</tkcontrols:Case>
|
||||
|
||||
<!-- Mouse Button Trigger -->
|
||||
<tkcontrols:Case Value="Mouse">
|
||||
<ComboBox
|
||||
x:Name="MouseTriggerComboBox"
|
||||
MinHeight="86"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalContentAlignment="Center">
|
||||
<ComboBoxItem Content="Left mouse button" Tag="LeftMouse" />
|
||||
<ComboBoxItem Content="Right mouse button" Tag="RightMouse" />
|
||||
<ComboBoxItem Content="Scroll up" Tag="ScrollUp" />
|
||||
<ComboBoxItem Content="Scroll down" Tag="ScrollDown" />
|
||||
</ComboBox>
|
||||
</tkcontrols:Case>
|
||||
</tkcontrols:SwitchPresenter>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Arrow Separator -->
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="24,0,24,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
FontSize="20"
|
||||
Text="" />
|
||||
|
||||
<!-- Right Column: Action Output -->
|
||||
<StackPanel
|
||||
Grid.Column="2"
|
||||
MinWidth="360"
|
||||
Orientation="Vertical"
|
||||
Spacing="8">
|
||||
<TextBlock
|
||||
x:Uid="UnifiedMappingControlActionTextBlock"
|
||||
FontWeight="SemiBold"
|
||||
Text="Action" />
|
||||
|
||||
<ComboBox
|
||||
x:Name="ActionTypeComboBox"
|
||||
HorizontalAlignment="Stretch"
|
||||
SelectionChanged="ActionTypeComboBox_SelectionChanged">
|
||||
<ComboBoxItem Content="Key or shortcut" Tag="KeyOrShortcut" IsSelected="True" />
|
||||
<ComboBoxItem Content="Text" Tag="Text" />
|
||||
<ComboBoxItem Content="Open URL" Tag="OpenUrl" />
|
||||
<ComboBoxItem Content="Open app" Tag="OpenApp" />
|
||||
<ComboBoxItem Content="Mouse click" Tag="MouseClick" />
|
||||
</ComboBox>
|
||||
|
||||
<tkcontrols:SwitchPresenter
|
||||
x:Name="ActionSwitchPresenter"
|
||||
TargetType="x:String"
|
||||
Value="{Binding SelectedItem.Tag, ElementName=ActionTypeComboBox}">
|
||||
|
||||
<!-- Key or Shortcut Action -->
|
||||
<tkcontrols:Case Value="KeyOrShortcut">
|
||||
<StackPanel Orientation="Vertical" Spacing="8">
|
||||
<ToggleButton
|
||||
x:Name="ActionKeyToggleBtn"
|
||||
MinHeight="86"
|
||||
Padding="8,24,8,24"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Center"
|
||||
VerticalContentAlignment="Center"
|
||||
Checked="ActionKeyToggleBtn_Checked"
|
||||
Style="{StaticResource CustomShortcutToggleButtonStyle}"
|
||||
Unchecked="ActionKeyToggleBtn_Unchecked">
|
||||
<ToggleButton.Content>
|
||||
<ItemsControl x:Name="ActionKeys">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<controls:WrapPanel
|
||||
HorizontalSpacing="4"
|
||||
Orientation="Horizontal"
|
||||
VerticalSpacing="4" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<local:KeyVisual Content="{Binding}" Style="{StaticResource AccentKeyVisualStyle}" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ToggleButton.Content>
|
||||
</ToggleButton>
|
||||
|
||||
<!-- App-specific remapping (only for shortcuts) -->
|
||||
<CheckBox
|
||||
x:Name="AppSpecificCheckBox"
|
||||
x:Uid="UnifiedMappingControlAppSpecificCheckBox"
|
||||
Content="Make this app-specific"
|
||||
IsEnabled="False" />
|
||||
<TextBox
|
||||
x:Name="AppNameTextBox"
|
||||
x:Uid="UnifiedMappingControlAppNameTextBox"
|
||||
Background="{ThemeResource TextControlBackgroundFocused}"
|
||||
BorderBrush="{ThemeResource ControlStrokeColorDefaultBrush}"
|
||||
GotFocus="AppNameTextBox_GotFocus"
|
||||
PlaceholderText="Enter app name (e.g., notepad.exe)"
|
||||
Visibility="Collapsed" />
|
||||
</StackPanel>
|
||||
</tkcontrols:Case>
|
||||
|
||||
<!-- Text Action -->
|
||||
<tkcontrols:Case Value="Text">
|
||||
<StackPanel Orientation="Vertical" Spacing="8">
|
||||
<TextBox
|
||||
x:Name="TextContentBox"
|
||||
x:Uid="UnifiedMappingControlTextContentTextBox"
|
||||
Height="120"
|
||||
AcceptsReturn="True"
|
||||
Background="{ThemeResource TextControlBackgroundFocused}"
|
||||
BorderBrush="{ThemeResource ControlStrokeColorDefaultBrush}"
|
||||
FontSize="13"
|
||||
GotFocus="TextContentBox_GotFocus"
|
||||
Header="Text to type"
|
||||
PlaceholderText="Enter the text to type when triggered"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<!-- App-specific for text -->
|
||||
<CheckBox
|
||||
x:Name="TextAppSpecificCheckBox"
|
||||
x:Uid="UnifiedMappingControlTextAppSpecificCheckBox"
|
||||
Content="Make this app-specific"
|
||||
IsEnabled="False" />
|
||||
<TextBox
|
||||
x:Name="TextAppNameTextBox"
|
||||
x:Uid="UnifiedMappingControlTextAppNameTextBox"
|
||||
Background="{ThemeResource TextControlBackgroundFocused}"
|
||||
BorderBrush="{ThemeResource ControlStrokeColorDefaultBrush}"
|
||||
GotFocus="TextAppNameTextBox_GotFocus"
|
||||
PlaceholderText="Enter app name (e.g., notepad.exe)"
|
||||
Visibility="Collapsed" />
|
||||
</StackPanel>
|
||||
</tkcontrols:Case>
|
||||
|
||||
<!-- Open URL Action -->
|
||||
<tkcontrols:Case Value="OpenUrl">
|
||||
<TextBox
|
||||
x:Name="UrlPathInput"
|
||||
x:Uid="UnifiedMappingControlUrlPathTextBox"
|
||||
Background="{ThemeResource TextControlBackgroundFocused}"
|
||||
BorderBrush="{ThemeResource ControlStrokeColorDefaultBrush}"
|
||||
FontSize="13"
|
||||
GotFocus="UrlPathInput_GotFocus"
|
||||
Header="URL to open"
|
||||
PlaceholderText="https://example.com" />
|
||||
</tkcontrols:Case>
|
||||
|
||||
<!-- Open App Action -->
|
||||
<tkcontrols:Case Value="OpenApp">
|
||||
<StackPanel Orientation="Vertical" Spacing="8">
|
||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||
<TextBox
|
||||
x:Name="ProgramPathInput"
|
||||
x:Uid="UnifiedMappingControlProgramPathTextBox"
|
||||
Width="240"
|
||||
GotFocus="ProgramPathInput_GotFocus"
|
||||
Header="Program path"
|
||||
PlaceholderText="C:\Program Files\..." />
|
||||
<Button
|
||||
x:Name="ProgramPathSelectButton"
|
||||
x:Uid="UnifiedMappingControlPathSelectButton"
|
||||
VerticalAlignment="Bottom"
|
||||
Click="ProgramPathSelectButton_Click"
|
||||
Content="Browse..." />
|
||||
</StackPanel>
|
||||
|
||||
<TextBox
|
||||
x:Name="ProgramArgsInput"
|
||||
x:Uid="UnifiedMappingControlArgumentsTextBox"
|
||||
GotFocus="ProgramArgsInput_GotFocus"
|
||||
Header="Arguments (optional)"
|
||||
PlaceholderText="--arg1 value1" />
|
||||
|
||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||
<TextBox
|
||||
x:Name="StartInPathInput"
|
||||
x:Uid="UnifiedMappingControlStartInTextBox"
|
||||
Width="240"
|
||||
GotFocus="StartInPathInput_GotFocus"
|
||||
Header="Start in directory (optional)"
|
||||
PlaceholderText="C:\Users\..." />
|
||||
<Button
|
||||
x:Name="StartInSelectButton"
|
||||
x:Uid="UnifiedMappingControlStartInSelectButton"
|
||||
VerticalAlignment="Bottom"
|
||||
Click="StartInSelectButton_Click"
|
||||
Content="Browse..." />
|
||||
</StackPanel>
|
||||
|
||||
<ComboBox
|
||||
x:Name="ElevationComboBox"
|
||||
x:Uid="UnifiedMappingControlElevationComboBox"
|
||||
HorizontalAlignment="Stretch"
|
||||
Header="Run as"
|
||||
SelectedIndex="0">
|
||||
<x:String>Normal</x:String>
|
||||
<x:String>Elevated</x:String>
|
||||
<x:String>Different user</x:String>
|
||||
</ComboBox>
|
||||
|
||||
<ComboBox
|
||||
x:Name="IfRunningComboBox"
|
||||
x:Uid="UnifiedMappingControlIfRunningComboBox"
|
||||
HorizontalAlignment="Stretch"
|
||||
Header="If already running"
|
||||
SelectedIndex="0">
|
||||
<x:String>Show window</x:String>
|
||||
<x:String>Start another</x:String>
|
||||
<x:String>Do nothing</x:String>
|
||||
<x:String>Close</x:String>
|
||||
<x:String>End task</x:String>
|
||||
</ComboBox>
|
||||
|
||||
<ComboBox
|
||||
x:Name="VisibilityComboBox"
|
||||
x:Uid="UnifiedMappingControlVisibilityComboBox"
|
||||
HorizontalAlignment="Stretch"
|
||||
Header="Window visibility"
|
||||
SelectedIndex="0">
|
||||
<x:String>Normal</x:String>
|
||||
<x:String>Hidden</x:String>
|
||||
<x:String>Minimized</x:String>
|
||||
<x:String>Maximized</x:String>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</tkcontrols:Case>
|
||||
|
||||
<!-- Mouse Click Action (Placeholder) -->
|
||||
<tkcontrols:Case Value="MouseClick">
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="Mouse click action - coming soon"
|
||||
TextWrapping="Wrap" />
|
||||
</tkcontrols:Case>
|
||||
</tkcontrols:SwitchPresenter>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,943 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using KeyboardManagerEditorUI.Helpers;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.Pickers;
|
||||
using Windows.System;
|
||||
using WinRT.Interop;
|
||||
using static KeyboardManagerEditorUI.Interop.ShortcutKeyMapping;
|
||||
|
||||
#pragma warning disable SA1124 // Do not use regions
|
||||
|
||||
namespace KeyboardManagerEditorUI.Controls
|
||||
{
|
||||
/// <summary>
|
||||
/// Unified control that consolidates all mapping input types:
|
||||
/// - Key/Shortcut remapping (InputControl)
|
||||
/// - Text output (TextPageInputControl)
|
||||
/// - URL opening (UrlPageInputControl)
|
||||
/// - App launching (AppPageInputControl)
|
||||
/// </summary>
|
||||
public sealed partial class UnifiedMappingControl : UserControl, IDisposable, IKeyboardHookTarget
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private readonly ObservableCollection<string> _triggerKeys = new();
|
||||
private readonly ObservableCollection<string> _actionKeys = new();
|
||||
|
||||
private TeachingTip? _currentNotification;
|
||||
private DispatcherTimer? _notificationTimer;
|
||||
|
||||
private bool _disposed;
|
||||
private bool _internalUpdate;
|
||||
|
||||
private KeyInputMode _currentInputMode = KeyInputMode.OriginalKeys;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Enums
|
||||
|
||||
/// <summary>
|
||||
/// Defines the type of trigger for the mapping.
|
||||
/// </summary>
|
||||
public enum TriggerType
|
||||
{
|
||||
KeyOrShortcut,
|
||||
Mouse,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the type of action to perform.
|
||||
/// </summary>
|
||||
public enum ActionType
|
||||
{
|
||||
KeyOrShortcut,
|
||||
Text,
|
||||
OpenUrl,
|
||||
OpenApp,
|
||||
MouseClick,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the mouse button options.
|
||||
/// </summary>
|
||||
public enum MouseButton
|
||||
{
|
||||
LeftMouse,
|
||||
RightMouse,
|
||||
ScrollUp,
|
||||
ScrollDown,
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current trigger type.
|
||||
/// </summary>
|
||||
public TriggerType CurrentTriggerType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (TriggerTypeComboBox?.SelectedItem is ComboBoxItem item)
|
||||
{
|
||||
return item.Tag?.ToString() switch
|
||||
{
|
||||
"Mouse" => TriggerType.Mouse,
|
||||
_ => TriggerType.KeyOrShortcut,
|
||||
};
|
||||
}
|
||||
|
||||
return TriggerType.KeyOrShortcut;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current action type.
|
||||
/// </summary>
|
||||
public ActionType CurrentActionType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (ActionTypeComboBox?.SelectedItem is ComboBoxItem item)
|
||||
{
|
||||
return item.Tag?.ToString() switch
|
||||
{
|
||||
"Text" => ActionType.Text,
|
||||
"OpenUrl" => ActionType.OpenUrl,
|
||||
"OpenApp" => ActionType.OpenApp,
|
||||
"MouseClick" => ActionType.MouseClick,
|
||||
_ => ActionType.KeyOrShortcut,
|
||||
};
|
||||
}
|
||||
|
||||
return ActionType.KeyOrShortcut;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
public UnifiedMappingControl()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
TriggerKeys.ItemsSource = _triggerKeys;
|
||||
ActionKeys.ItemsSource = _actionKeys;
|
||||
|
||||
this.Unloaded += UnifiedMappingControl_Unloaded;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lifecycle Events
|
||||
|
||||
private void UserControl_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Set up event handlers for app-specific checkboxes
|
||||
AppSpecificCheckBox.Checked += AppSpecificCheckBox_Changed;
|
||||
AppSpecificCheckBox.Unchecked += AppSpecificCheckBox_Changed;
|
||||
TextAppSpecificCheckBox.Checked += TextAppSpecificCheckBox_Changed;
|
||||
TextAppSpecificCheckBox.Unchecked += TextAppSpecificCheckBox_Changed;
|
||||
|
||||
// Activate keyboard hook for the trigger input
|
||||
if (TriggerKeyToggleBtn.IsChecked == true)
|
||||
{
|
||||
_currentInputMode = KeyInputMode.OriginalKeys;
|
||||
KeyboardHookHelper.Instance.ActivateHook(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void UnifiedMappingControl_Unloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Reset();
|
||||
CleanupKeyboardHook();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Trigger Type Handling
|
||||
|
||||
private void TriggerTypeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (TriggerTypeComboBox?.SelectedItem is ComboBoxItem item)
|
||||
{
|
||||
string? tag = item.Tag?.ToString();
|
||||
|
||||
// Cleanup keyboard hook when switching to mouse
|
||||
if (tag == "Mouse")
|
||||
{
|
||||
CleanupKeyboardHook();
|
||||
UncheckAllToggleButtons();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TriggerKeyToggleBtn_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (TriggerKeyToggleBtn.IsChecked == true)
|
||||
{
|
||||
_currentInputMode = KeyInputMode.OriginalKeys;
|
||||
|
||||
// Uncheck action toggle if checked
|
||||
if (ActionKeyToggleBtn?.IsChecked == true)
|
||||
{
|
||||
ActionKeyToggleBtn.IsChecked = false;
|
||||
}
|
||||
|
||||
KeyboardHookHelper.Instance.ActivateHook(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void TriggerKeyToggleBtn_Unchecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_currentInputMode == KeyInputMode.OriginalKeys)
|
||||
{
|
||||
CleanupKeyboardHook();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Action Type Handling
|
||||
|
||||
private void ActionTypeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (ActionTypeComboBox?.SelectedItem is ComboBoxItem item)
|
||||
{
|
||||
string? tag = item.Tag?.ToString();
|
||||
|
||||
// Cleanup keyboard hook when switching away from key/shortcut
|
||||
if (tag != "KeyOrShortcut")
|
||||
{
|
||||
if (_currentInputMode == KeyInputMode.RemappedKeys)
|
||||
{
|
||||
CleanupKeyboardHook();
|
||||
}
|
||||
|
||||
if (ActionKeyToggleBtn?.IsChecked == true)
|
||||
{
|
||||
ActionKeyToggleBtn.IsChecked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ActionKeyToggleBtn_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (ActionKeyToggleBtn.IsChecked == true)
|
||||
{
|
||||
_currentInputMode = KeyInputMode.RemappedKeys;
|
||||
|
||||
// Uncheck trigger toggle if checked
|
||||
if (TriggerKeyToggleBtn?.IsChecked == true)
|
||||
{
|
||||
TriggerKeyToggleBtn.IsChecked = false;
|
||||
}
|
||||
|
||||
KeyboardHookHelper.Instance.ActivateHook(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void ActionKeyToggleBtn_Unchecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_currentInputMode == KeyInputMode.RemappedKeys)
|
||||
{
|
||||
CleanupKeyboardHook();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region App-Specific Handling
|
||||
|
||||
private void AppSpecificCheckBox_Changed(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_internalUpdate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CleanupKeyboardHook();
|
||||
UncheckAllToggleButtons();
|
||||
|
||||
AppNameTextBox.Visibility = AppSpecificCheckBox.IsChecked == true
|
||||
? Visibility.Visible
|
||||
: Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private void TextAppSpecificCheckBox_Changed(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_internalUpdate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CleanupKeyboardHook();
|
||||
UncheckAllToggleButtons();
|
||||
|
||||
TextAppNameTextBox.Visibility = TextAppSpecificCheckBox.IsChecked == true
|
||||
? Visibility.Visible
|
||||
: Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private void UpdateAppSpecificCheckBoxState()
|
||||
{
|
||||
// Only enable app-specific remapping for shortcuts (multiple keys)
|
||||
bool isShortcut = _triggerKeys.Count > 1;
|
||||
|
||||
try
|
||||
{
|
||||
_internalUpdate = true;
|
||||
|
||||
// Update Key/Shortcut action checkbox
|
||||
AppSpecificCheckBox.IsEnabled = isShortcut;
|
||||
if (!isShortcut)
|
||||
{
|
||||
AppSpecificCheckBox.IsChecked = false;
|
||||
AppNameTextBox.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
// Update Text action checkbox
|
||||
TextAppSpecificCheckBox.IsEnabled = isShortcut;
|
||||
if (!isShortcut)
|
||||
{
|
||||
TextAppSpecificCheckBox.IsChecked = false;
|
||||
TextAppNameTextBox.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_internalUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region TextBox Focus Handlers
|
||||
|
||||
private void AppNameTextBox_GotFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
CleanupKeyboardHook();
|
||||
UncheckAllToggleButtons();
|
||||
}
|
||||
|
||||
private void TextContentBox_GotFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
CleanupKeyboardHook();
|
||||
UncheckAllToggleButtons();
|
||||
}
|
||||
|
||||
private void TextAppNameTextBox_GotFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
CleanupKeyboardHook();
|
||||
UncheckAllToggleButtons();
|
||||
}
|
||||
|
||||
private void UrlPathInput_GotFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
CleanupKeyboardHook();
|
||||
UncheckAllToggleButtons();
|
||||
}
|
||||
|
||||
private void ProgramPathInput_GotFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
CleanupKeyboardHook();
|
||||
UncheckAllToggleButtons();
|
||||
}
|
||||
|
||||
private void ProgramArgsInput_GotFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
CleanupKeyboardHook();
|
||||
UncheckAllToggleButtons();
|
||||
}
|
||||
|
||||
private void StartInPathInput_GotFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
CleanupKeyboardHook();
|
||||
UncheckAllToggleButtons();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region File/Folder Pickers
|
||||
|
||||
private async void ProgramPathSelectButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var picker = new FileOpenPicker();
|
||||
|
||||
var hwnd = WindowNative.GetWindowHandle(App.MainWindow);
|
||||
InitializeWithWindow.Initialize(picker, hwnd);
|
||||
|
||||
picker.FileTypeFilter.Add(".exe");
|
||||
|
||||
StorageFile file = await picker.PickSingleFileAsync();
|
||||
|
||||
if (file != null)
|
||||
{
|
||||
ProgramPathInput.Text = file.Path;
|
||||
}
|
||||
}
|
||||
|
||||
private async void StartInSelectButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var picker = new FolderPicker();
|
||||
|
||||
var hwnd = WindowNative.GetWindowHandle(App.MainWindow);
|
||||
InitializeWithWindow.Initialize(picker, hwnd);
|
||||
|
||||
picker.FileTypeFilter.Add("*");
|
||||
|
||||
StorageFolder folder = await picker.PickSingleFolderAsync();
|
||||
|
||||
if (folder != null)
|
||||
{
|
||||
StartInPathInput.Text = folder.Path;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IKeyboardHookTarget Implementation
|
||||
|
||||
public void OnKeyDown(VirtualKey key, List<string> formattedKeys)
|
||||
{
|
||||
if (_currentInputMode == KeyInputMode.OriginalKeys)
|
||||
{
|
||||
_triggerKeys.Clear();
|
||||
foreach (var keyName in formattedKeys)
|
||||
{
|
||||
_triggerKeys.Add(keyName);
|
||||
}
|
||||
|
||||
UpdateAppSpecificCheckBoxState();
|
||||
}
|
||||
else if (_currentInputMode == KeyInputMode.RemappedKeys)
|
||||
{
|
||||
_actionKeys.Clear();
|
||||
foreach (var keyName in formattedKeys)
|
||||
{
|
||||
_actionKeys.Add(keyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearKeys()
|
||||
{
|
||||
if (_currentInputMode == KeyInputMode.OriginalKeys)
|
||||
{
|
||||
_triggerKeys.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
_actionKeys.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnInputLimitReached()
|
||||
{
|
||||
ShowNotificationTip("Shortcuts can only have up to 4 modifier keys");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public API - Getters
|
||||
|
||||
/// <summary>
|
||||
/// Gets the trigger keys.
|
||||
/// </summary>
|
||||
public List<string> GetTriggerKeys() => _triggerKeys.ToList();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the action keys (for Key/Shortcut action type).
|
||||
/// </summary>
|
||||
public List<string> GetActionKeys() => _actionKeys.ToList();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the selected mouse trigger.
|
||||
/// </summary>
|
||||
public MouseButton? GetMouseTrigger()
|
||||
{
|
||||
if (MouseTriggerComboBox?.SelectedItem is ComboBoxItem item)
|
||||
{
|
||||
return item.Tag?.ToString() switch
|
||||
{
|
||||
"LeftMouse" => MouseButton.LeftMouse,
|
||||
"RightMouse" => MouseButton.RightMouse,
|
||||
"ScrollUp" => MouseButton.ScrollUp,
|
||||
"ScrollDown" => MouseButton.ScrollDown,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the text content (for Text action type).
|
||||
/// </summary>
|
||||
public string GetTextContent() => TextContentBox?.Text ?? string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the URL (for OpenUrl action type).
|
||||
/// </summary>
|
||||
public string GetUrl() => UrlPathInput?.Text ?? string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the program path (for OpenApp action type).
|
||||
/// </summary>
|
||||
public string GetProgramPath() => ProgramPathInput?.Text ?? string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the program arguments (for OpenApp action type).
|
||||
/// </summary>
|
||||
public string GetProgramArgs() => ProgramArgsInput?.Text ?? string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the start-in directory (for OpenApp action type).
|
||||
/// </summary>
|
||||
public string GetStartInDirectory() => StartInPathInput?.Text ?? string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the mapping is app-specific.
|
||||
/// </summary>
|
||||
public bool GetIsAppSpecific()
|
||||
{
|
||||
return CurrentActionType switch
|
||||
{
|
||||
ActionType.KeyOrShortcut => AppSpecificCheckBox?.IsChecked ?? false,
|
||||
ActionType.Text => TextAppSpecificCheckBox?.IsChecked ?? false,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the app name for app-specific mappings.
|
||||
/// </summary>
|
||||
public string GetAppName()
|
||||
{
|
||||
return CurrentActionType switch
|
||||
{
|
||||
ActionType.KeyOrShortcut => GetIsAppSpecific() ? (AppNameTextBox?.Text ?? string.Empty) : string.Empty,
|
||||
ActionType.Text => GetIsAppSpecific() ? (TextAppNameTextBox?.Text ?? string.Empty) : string.Empty,
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the elevation level (for OpenApp action type).
|
||||
/// </summary>
|
||||
public ElevationLevel GetElevationLevel() => (ElevationLevel)(ElevationComboBox?.SelectedIndex ?? 0);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the window visibility (for OpenApp action type).
|
||||
/// </summary>
|
||||
public StartWindowType GetVisibility() => (StartWindowType)(VisibilityComboBox?.SelectedIndex ?? 0);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the if-running action (for OpenApp action type).
|
||||
/// </summary>
|
||||
public ProgramAlreadyRunningAction GetIfRunningAction() => (ProgramAlreadyRunningAction)(IfRunningComboBox?.SelectedIndex ?? 0);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public API - Setters
|
||||
|
||||
/// <summary>
|
||||
/// Sets the trigger keys.
|
||||
/// </summary>
|
||||
public void SetTriggerKeys(List<string> keys)
|
||||
{
|
||||
_triggerKeys.Clear();
|
||||
if (keys != null)
|
||||
{
|
||||
foreach (var key in keys)
|
||||
{
|
||||
_triggerKeys.Add(key);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateAppSpecificCheckBoxState();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the action keys.
|
||||
/// </summary>
|
||||
public void SetActionKeys(List<string> keys)
|
||||
{
|
||||
_actionKeys.Clear();
|
||||
if (keys != null)
|
||||
{
|
||||
foreach (var key in keys)
|
||||
{
|
||||
_actionKeys.Add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the action type.
|
||||
/// </summary>
|
||||
public void SetActionType(ActionType actionType)
|
||||
{
|
||||
int index = actionType switch
|
||||
{
|
||||
ActionType.Text => 1,
|
||||
ActionType.OpenUrl => 2,
|
||||
ActionType.OpenApp => 3,
|
||||
ActionType.MouseClick => 4,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
if (ActionTypeComboBox != null)
|
||||
{
|
||||
ActionTypeComboBox.SelectedIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the text content (for Text action type).
|
||||
/// </summary>
|
||||
public void SetTextContent(string text)
|
||||
{
|
||||
if (TextContentBox != null)
|
||||
{
|
||||
TextContentBox.Text = text;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the URL (for OpenUrl action type).
|
||||
/// </summary>
|
||||
public void SetUrl(string url)
|
||||
{
|
||||
if (UrlPathInput != null)
|
||||
{
|
||||
UrlPathInput.Text = url;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the program path (for OpenApp action type).
|
||||
/// </summary>
|
||||
public void SetProgramPath(string path)
|
||||
{
|
||||
if (ProgramPathInput != null)
|
||||
{
|
||||
ProgramPathInput.Text = path;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the program arguments (for OpenApp action type).
|
||||
/// </summary>
|
||||
public void SetProgramArgs(string args)
|
||||
{
|
||||
if (ProgramArgsInput != null)
|
||||
{
|
||||
ProgramArgsInput.Text = args;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the start-in directory (for OpenApp action type).
|
||||
/// </summary>
|
||||
public void SetStartInDirectory(string path)
|
||||
{
|
||||
if (StartInPathInput != null)
|
||||
{
|
||||
StartInPathInput.Text = path;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the elevation level (for OpenApp action type).
|
||||
/// </summary>
|
||||
public void SetElevationLevel(ElevationLevel elevationLevel)
|
||||
{
|
||||
if (ElevationComboBox != null)
|
||||
{
|
||||
ElevationComboBox.SelectedIndex = (int)elevationLevel;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the window visibility (for OpenApp action type).
|
||||
/// </summary>
|
||||
public void SetVisibility(StartWindowType visibility)
|
||||
{
|
||||
if (VisibilityComboBox != null)
|
||||
{
|
||||
VisibilityComboBox.SelectedIndex = (int)visibility;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the if-already-running action (for OpenApp action type).
|
||||
/// </summary>
|
||||
public void SetIfRunningAction(ProgramAlreadyRunningAction ifRunningAction)
|
||||
{
|
||||
if (IfRunningComboBox != null)
|
||||
{
|
||||
IfRunningComboBox.SelectedIndex = (int)ifRunningAction;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets whether the mapping is app-specific.
|
||||
/// </summary>
|
||||
public void SetAppSpecific(bool isAppSpecific, string appName)
|
||||
{
|
||||
switch (CurrentActionType)
|
||||
{
|
||||
case ActionType.KeyOrShortcut:
|
||||
if (AppSpecificCheckBox != null)
|
||||
{
|
||||
AppSpecificCheckBox.IsChecked = isAppSpecific;
|
||||
if (isAppSpecific && AppNameTextBox != null)
|
||||
{
|
||||
AppNameTextBox.Text = appName;
|
||||
AppNameTextBox.Visibility = Visibility.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ActionType.Text:
|
||||
if (TextAppSpecificCheckBox != null)
|
||||
{
|
||||
TextAppSpecificCheckBox.IsChecked = isAppSpecific;
|
||||
if (isAppSpecific && TextAppNameTextBox != null)
|
||||
{
|
||||
TextAppNameTextBox.Text = appName;
|
||||
TextAppNameTextBox.Visibility = Visibility.Visible;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper Methods
|
||||
|
||||
private void UncheckAllToggleButtons()
|
||||
{
|
||||
if (TriggerKeyToggleBtn?.IsChecked == true)
|
||||
{
|
||||
TriggerKeyToggleBtn.IsChecked = false;
|
||||
}
|
||||
|
||||
if (ActionKeyToggleBtn?.IsChecked == true)
|
||||
{
|
||||
ActionKeyToggleBtn.IsChecked = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void CleanupKeyboardHook()
|
||||
{
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets all inputs to their default state.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
_triggerKeys.Clear();
|
||||
_actionKeys.Clear();
|
||||
|
||||
UncheckAllToggleButtons();
|
||||
|
||||
_currentInputMode = KeyInputMode.OriginalKeys;
|
||||
|
||||
// Reset combo boxes
|
||||
if (TriggerTypeComboBox != null)
|
||||
{
|
||||
TriggerTypeComboBox.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
if (ActionTypeComboBox != null)
|
||||
{
|
||||
ActionTypeComboBox.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
if (MouseTriggerComboBox != null)
|
||||
{
|
||||
MouseTriggerComboBox.SelectedIndex = -1;
|
||||
}
|
||||
|
||||
// Reset text inputs
|
||||
if (TextContentBox != null)
|
||||
{
|
||||
TextContentBox.Text = string.Empty;
|
||||
}
|
||||
|
||||
if (UrlPathInput != null)
|
||||
{
|
||||
UrlPathInput.Text = string.Empty;
|
||||
}
|
||||
|
||||
if (ProgramPathInput != null)
|
||||
{
|
||||
ProgramPathInput.Text = string.Empty;
|
||||
}
|
||||
|
||||
if (ProgramArgsInput != null)
|
||||
{
|
||||
ProgramArgsInput.Text = string.Empty;
|
||||
}
|
||||
|
||||
if (StartInPathInput != null)
|
||||
{
|
||||
StartInPathInput.Text = string.Empty;
|
||||
}
|
||||
|
||||
if (AppNameTextBox != null)
|
||||
{
|
||||
AppNameTextBox.Text = string.Empty;
|
||||
AppNameTextBox.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
if (TextAppNameTextBox != null)
|
||||
{
|
||||
TextAppNameTextBox.Text = string.Empty;
|
||||
TextAppNameTextBox.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
// Reset checkboxes
|
||||
if (AppSpecificCheckBox != null)
|
||||
{
|
||||
AppSpecificCheckBox.IsChecked = false;
|
||||
AppSpecificCheckBox.IsEnabled = false;
|
||||
}
|
||||
|
||||
if (TextAppSpecificCheckBox != null)
|
||||
{
|
||||
TextAppSpecificCheckBox.IsChecked = false;
|
||||
TextAppSpecificCheckBox.IsEnabled = false;
|
||||
}
|
||||
|
||||
// Reset app combo boxes
|
||||
if (ElevationComboBox != null)
|
||||
{
|
||||
ElevationComboBox.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
if (IfRunningComboBox != null)
|
||||
{
|
||||
IfRunningComboBox.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
if (VisibilityComboBox != null)
|
||||
{
|
||||
VisibilityComboBox.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
CloseExistingNotification();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets only the toggle buttons without clearing the key displays.
|
||||
/// </summary>
|
||||
public void ResetToggleButtons()
|
||||
{
|
||||
UncheckAllToggleButtons();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Notifications
|
||||
|
||||
public void ShowNotificationTip(string message)
|
||||
{
|
||||
CloseExistingNotification();
|
||||
|
||||
_currentNotification = new TeachingTip
|
||||
{
|
||||
Title = "Input Limit Reached",
|
||||
Subtitle = message,
|
||||
IsLightDismissEnabled = true,
|
||||
PreferredPlacement = TeachingTipPlacementMode.Top,
|
||||
XamlRoot = this.XamlRoot,
|
||||
IconSource = new SymbolIconSource { Symbol = Symbol.Important },
|
||||
};
|
||||
|
||||
// Target the appropriate toggle button
|
||||
_currentNotification.Target = _currentInputMode == KeyInputMode.RemappedKeys
|
||||
? ActionKeyToggleBtn
|
||||
: TriggerKeyToggleBtn;
|
||||
|
||||
if (this.Content is Panel rootPanel)
|
||||
{
|
||||
rootPanel.Children.Add(_currentNotification);
|
||||
_currentNotification.IsOpen = true;
|
||||
|
||||
_notificationTimer = new DispatcherTimer
|
||||
{
|
||||
Interval = TimeSpan.FromMilliseconds(EditorConstants.DefaultNotificationTimeout),
|
||||
};
|
||||
_notificationTimer.Tick += (s, e) => CloseExistingNotification();
|
||||
_notificationTimer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseExistingNotification()
|
||||
{
|
||||
_notificationTimer?.Stop();
|
||||
_notificationTimer = null;
|
||||
|
||||
if (_currentNotification != null && _currentNotification.IsOpen)
|
||||
{
|
||||
_currentNotification.IsOpen = false;
|
||||
|
||||
if (this.Content is Panel rootPanel && rootPanel.Children.Contains(_currentNotification))
|
||||
{
|
||||
rootPanel.Children.Remove(_currentNotification);
|
||||
}
|
||||
|
||||
_currentNotification = null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
CleanupKeyboardHook();
|
||||
CloseExistingNotification();
|
||||
Reset();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning restore SA1124 // Do not use regions
|
||||
@@ -46,6 +46,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.Common" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.Controls.Primitives" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" />
|
||||
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls.Markdown" />
|
||||
|
||||
@@ -12,14 +12,33 @@
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Padding="16">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="64" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Add New Remapping Button -->
|
||||
<Button
|
||||
x:Name="NewRemappingBtn"
|
||||
Grid.Row="0"
|
||||
Height="36"
|
||||
Margin="0,0,0,16"
|
||||
Click="NewRemappingBtn_Click">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<FontIcon
|
||||
FontSize="14"
|
||||
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
|
||||
Glyph="" />
|
||||
<TextBlock VerticalAlignment="Center" Text="Add new remapping" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Orientation="Vertical" Spacing="24">
|
||||
|
||||
<!-- Remappings Section -->
|
||||
<!-- Remappings Section -->
|
||||
<StackPanel Orientation="Vertical" Spacing="12">
|
||||
<TextBlock
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="Remappings" />
|
||||
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="Remappings" />
|
||||
<Grid
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="{ThemeResource LayerFillColorDefaultBrush}"
|
||||
@@ -73,7 +92,8 @@
|
||||
<ListView
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="4"
|
||||
IsItemClickEnabled="False"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="RemappingsList_ItemClick"
|
||||
ItemsSource="{x:Bind RemappingList}"
|
||||
SelectionMode="None">
|
||||
<ListView.ItemTemplate>
|
||||
@@ -152,11 +172,9 @@
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Text Section -->
|
||||
<!-- Text Section -->
|
||||
<StackPanel Orientation="Vertical" Spacing="12">
|
||||
<TextBlock
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="Text" />
|
||||
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="Text" />
|
||||
<Grid
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="{ThemeResource LayerFillColorDefaultBrush}"
|
||||
@@ -211,7 +229,8 @@
|
||||
<ListView
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="4"
|
||||
IsItemClickEnabled="False"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="TextMappingsList_ItemClick"
|
||||
ItemsSource="{x:Bind TextMappings}"
|
||||
SelectionMode="None">
|
||||
<ListView.ItemTemplate>
|
||||
@@ -273,11 +292,9 @@
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<!-- Programs Section -->
|
||||
<!-- Programs Section -->
|
||||
<StackPanel Orientation="Vertical" Spacing="12">
|
||||
<TextBlock
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="Programs" />
|
||||
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="Programs" />
|
||||
<Grid
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="{ThemeResource LayerFillColorDefaultBrush}"
|
||||
@@ -320,7 +337,8 @@
|
||||
<ListView
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="4"
|
||||
IsItemClickEnabled="False"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="ProgramShortcutsList_ItemClick"
|
||||
ItemsSource="{x:Bind ProgramShortcuts}"
|
||||
SelectionMode="None">
|
||||
<ListView.ItemTemplate>
|
||||
@@ -362,9 +380,7 @@
|
||||
Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
Spacing="8">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Bind AppToRun}" />
|
||||
<TextBlock VerticalAlignment="Center" Text="{x:Bind AppToRun}" />
|
||||
<FontIcon
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
@@ -372,9 +388,7 @@
|
||||
Glyph="">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock>
|
||||
<Run
|
||||
x:Uid="ProgramsPageArgumentsRun"
|
||||
FontWeight="SemiBold" />
|
||||
<Run x:Uid="ProgramsPageArgumentsRun" FontWeight="SemiBold" />
|
||||
<Run Text="{x:Bind Args}" />
|
||||
</TextBlock>
|
||||
</ToolTipService.ToolTip>
|
||||
@@ -387,11 +401,9 @@
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<!-- URLs Section -->
|
||||
<!-- URLs Section -->
|
||||
<StackPanel Orientation="Vertical" Spacing="12">
|
||||
<TextBlock
|
||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||
Text="URLs" />
|
||||
<TextBlock Style="{StaticResource SubtitleTextBlockStyle}" Text="URLs" />
|
||||
<Grid
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="{ThemeResource LayerFillColorDefaultBrush}"
|
||||
@@ -434,7 +446,8 @@
|
||||
<ListView
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="4"
|
||||
IsItemClickEnabled="False"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="UrlShortcutsList_ItemClick"
|
||||
ItemsSource="{x:Bind UrlShortcuts}"
|
||||
SelectionMode="None">
|
||||
<ListView.ItemTemplate>
|
||||
@@ -485,5 +498,19 @@
|
||||
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- Content Dialog for new remapping -->
|
||||
<ContentDialog
|
||||
x:Name="RemappingDialog"
|
||||
Title="Add new remapping"
|
||||
MinWidth="800"
|
||||
MinHeight="500"
|
||||
MaxWidth="900"
|
||||
CloseButtonText="Cancel"
|
||||
DefaultButton="Primary"
|
||||
PrimaryButtonStyle="{StaticResource AccentButtonStyle}"
|
||||
PrimaryButtonText="Save">
|
||||
<controls:UnifiedMappingControl x:Name="UnifiedMappingControl" Margin="0,16,0,0" />
|
||||
</ContentDialog>
|
||||
</Grid>
|
||||
</Page>
|
||||
|
||||
@@ -5,24 +5,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using KeyboardManagerEditorUI.Controls;
|
||||
using KeyboardManagerEditorUI.Helpers;
|
||||
using KeyboardManagerEditorUI.Interop;
|
||||
using KeyboardManagerEditorUI.Settings;
|
||||
using ManagedCommon;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using static KeyboardManagerEditorUI.Interop.ShortcutKeyMapping;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// A consolidated page that displays all mappings from Remappings, Text, Programs, and URLs pages.
|
||||
/// </summary>
|
||||
#pragma warning disable SA1124 // Do not use regions
|
||||
public sealed partial class All : Page, IDisposable
|
||||
{
|
||||
private KeyboardMappingService? _mappingService;
|
||||
private bool _disposed;
|
||||
|
||||
// Edit mode tracking
|
||||
private bool _isEditMode;
|
||||
private EditingItem? _editingItem;
|
||||
|
||||
public ObservableCollection<Remapping> RemappingList { get; } = new ObservableCollection<Remapping>();
|
||||
|
||||
public ObservableCollection<TextMapping> TextMappings { get; } = new ObservableCollection<TextMapping>();
|
||||
@@ -34,6 +44,30 @@ namespace KeyboardManagerEditorUI.Pages
|
||||
[DllImport("PowerToys.KeyboardManagerEditorLibraryWrapper.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
|
||||
private static extern void GetKeyDisplayName(int keyCode, [Out] StringBuilder keyName, int maxLength);
|
||||
|
||||
/// <summary>
|
||||
/// Tracks what item is being edited and its type.
|
||||
/// </summary>
|
||||
private sealed class EditingItem
|
||||
{
|
||||
public enum ItemType
|
||||
{
|
||||
Remapping,
|
||||
TextMapping,
|
||||
ProgramShortcut,
|
||||
UrlShortcut,
|
||||
}
|
||||
|
||||
public ItemType Type { get; set; }
|
||||
|
||||
public object Item { get; set; } = null!;
|
||||
|
||||
public List<string> OriginalTriggerKeys { get; set; } = new();
|
||||
|
||||
public string? AppName { get; set; }
|
||||
|
||||
public bool IsAllApps { get; set; } = true;
|
||||
}
|
||||
|
||||
public All()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
@@ -51,11 +85,444 @@ namespace KeyboardManagerEditorUI.Pages
|
||||
this.Unloaded += All_Unloaded;
|
||||
}
|
||||
|
||||
private void All_Unloaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||
private void All_Unloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
#region Dialog Show Methods
|
||||
|
||||
private async void NewRemappingBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_isEditMode = false;
|
||||
_editingItem = null;
|
||||
|
||||
// Reset the control before showing
|
||||
UnifiedMappingControl.Reset();
|
||||
RemappingDialog.Title = "Add new remapping";
|
||||
|
||||
await ShowRemappingDialog();
|
||||
}
|
||||
|
||||
private async void RemappingsList_ItemClick(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
if (e.ClickedItem is Remapping remapping)
|
||||
{
|
||||
_isEditMode = true;
|
||||
_editingItem = new EditingItem
|
||||
{
|
||||
Type = EditingItem.ItemType.Remapping,
|
||||
Item = remapping,
|
||||
OriginalTriggerKeys = remapping.OriginalKeys.ToList(),
|
||||
AppName = remapping.AppName,
|
||||
IsAllApps = remapping.IsAllApps,
|
||||
};
|
||||
|
||||
UnifiedMappingControl.Reset();
|
||||
UnifiedMappingControl.SetTriggerKeys(remapping.OriginalKeys.ToList());
|
||||
UnifiedMappingControl.SetActionType(UnifiedMappingControl.ActionType.KeyOrShortcut);
|
||||
UnifiedMappingControl.SetActionKeys(remapping.RemappedKeys.ToList());
|
||||
UnifiedMappingControl.SetAppSpecific(!remapping.IsAllApps, remapping.AppName);
|
||||
|
||||
RemappingDialog.Title = "Edit remapping";
|
||||
await ShowRemappingDialog();
|
||||
}
|
||||
}
|
||||
|
||||
private async void TextMappingsList_ItemClick(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
if (e.ClickedItem is TextMapping textMapping)
|
||||
{
|
||||
_isEditMode = true;
|
||||
_editingItem = new EditingItem
|
||||
{
|
||||
Type = EditingItem.ItemType.TextMapping,
|
||||
Item = textMapping,
|
||||
OriginalTriggerKeys = textMapping.Keys.ToList(),
|
||||
AppName = textMapping.AppName,
|
||||
IsAllApps = textMapping.IsAllApps,
|
||||
};
|
||||
|
||||
UnifiedMappingControl.Reset();
|
||||
UnifiedMappingControl.SetTriggerKeys(textMapping.Keys.ToList());
|
||||
UnifiedMappingControl.SetActionType(UnifiedMappingControl.ActionType.Text);
|
||||
UnifiedMappingControl.SetTextContent(textMapping.Text);
|
||||
UnifiedMappingControl.SetAppSpecific(!textMapping.IsAllApps, textMapping.AppName);
|
||||
|
||||
RemappingDialog.Title = "Edit text mapping";
|
||||
await ShowRemappingDialog();
|
||||
}
|
||||
}
|
||||
|
||||
private async void ProgramShortcutsList_ItemClick(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
if (e.ClickedItem is ProgramShortcut programShortcut)
|
||||
{
|
||||
_isEditMode = true;
|
||||
_editingItem = new EditingItem
|
||||
{
|
||||
Type = EditingItem.ItemType.ProgramShortcut,
|
||||
Item = programShortcut,
|
||||
OriginalTriggerKeys = programShortcut.Shortcut.ToList(),
|
||||
};
|
||||
|
||||
UnifiedMappingControl.Reset();
|
||||
UnifiedMappingControl.SetTriggerKeys(programShortcut.Shortcut.ToList());
|
||||
UnifiedMappingControl.SetActionType(UnifiedMappingControl.ActionType.OpenApp);
|
||||
UnifiedMappingControl.SetProgramPath(programShortcut.AppToRun);
|
||||
UnifiedMappingControl.SetProgramArgs(programShortcut.Args);
|
||||
|
||||
// Load additional settings from SettingsManager if available
|
||||
if (!string.IsNullOrEmpty(programShortcut.Id) &&
|
||||
SettingsManager.EditorSettings.ShortcutSettingsDictionary.TryGetValue(programShortcut.Id, out var settings))
|
||||
{
|
||||
var mapping = settings.Shortcut;
|
||||
UnifiedMappingControl.SetStartInDirectory(mapping.StartInDirectory);
|
||||
UnifiedMappingControl.SetElevationLevel(mapping.Elevation);
|
||||
UnifiedMappingControl.SetVisibility(mapping.Visibility);
|
||||
UnifiedMappingControl.SetIfRunningAction(mapping.IfRunningAction);
|
||||
}
|
||||
|
||||
RemappingDialog.Title = "Edit program shortcut";
|
||||
await ShowRemappingDialog();
|
||||
}
|
||||
}
|
||||
|
||||
private async void UrlShortcutsList_ItemClick(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
if (e.ClickedItem is URLShortcut urlShortcut)
|
||||
{
|
||||
_isEditMode = true;
|
||||
_editingItem = new EditingItem
|
||||
{
|
||||
Type = EditingItem.ItemType.UrlShortcut,
|
||||
Item = urlShortcut,
|
||||
OriginalTriggerKeys = urlShortcut.Shortcut.ToList(),
|
||||
};
|
||||
|
||||
UnifiedMappingControl.Reset();
|
||||
UnifiedMappingControl.SetTriggerKeys(urlShortcut.Shortcut.ToList());
|
||||
UnifiedMappingControl.SetActionType(UnifiedMappingControl.ActionType.OpenUrl);
|
||||
UnifiedMappingControl.SetUrl(urlShortcut.URL);
|
||||
|
||||
RemappingDialog.Title = "Edit URL shortcut";
|
||||
await ShowRemappingDialog();
|
||||
}
|
||||
}
|
||||
|
||||
private async System.Threading.Tasks.Task ShowRemappingDialog()
|
||||
{
|
||||
// Hook up the primary button click handler
|
||||
RemappingDialog.PrimaryButtonClick += RemappingDialog_PrimaryButtonClick;
|
||||
|
||||
// Show the dialog
|
||||
await RemappingDialog.ShowAsync();
|
||||
|
||||
// Unhook the handler
|
||||
RemappingDialog.PrimaryButtonClick -= RemappingDialog_PrimaryButtonClick;
|
||||
|
||||
// Reset edit mode
|
||||
_isEditMode = false;
|
||||
_editingItem = null;
|
||||
|
||||
// Cleanup keyboard hook after dialog closes
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Save Logic
|
||||
private void RemappingDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
if (_mappingService == null)
|
||||
{
|
||||
Logger.LogError("Mapping service is null, cannot save mapping");
|
||||
args.Cancel = true;
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
bool saved = false;
|
||||
var actionType = UnifiedMappingControl.CurrentActionType;
|
||||
List<string> triggerKeys = UnifiedMappingControl.GetTriggerKeys();
|
||||
|
||||
if (triggerKeys == null || triggerKeys.Count == 0)
|
||||
{
|
||||
// No trigger keys specified
|
||||
args.Cancel = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// If in edit mode, delete the existing mapping first
|
||||
if (_isEditMode && _editingItem != null)
|
||||
{
|
||||
DeleteExistingMapping();
|
||||
}
|
||||
|
||||
switch (actionType)
|
||||
{
|
||||
case UnifiedMappingControl.ActionType.KeyOrShortcut:
|
||||
saved = SaveKeyOrShortcutMapping(triggerKeys);
|
||||
break;
|
||||
|
||||
case UnifiedMappingControl.ActionType.Text:
|
||||
saved = SaveTextMapping(triggerKeys);
|
||||
break;
|
||||
|
||||
case UnifiedMappingControl.ActionType.OpenUrl:
|
||||
saved = SaveUrlMapping(triggerKeys);
|
||||
break;
|
||||
|
||||
case UnifiedMappingControl.ActionType.OpenApp:
|
||||
saved = SaveProgramMapping(triggerKeys);
|
||||
break;
|
||||
|
||||
case UnifiedMappingControl.ActionType.MouseClick:
|
||||
// Not implemented yet
|
||||
args.Cancel = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (saved)
|
||||
{
|
||||
LoadAllMappings();
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Cancel = true;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Error saving mapping: " + ex.Message);
|
||||
args.Cancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteExistingMapping()
|
||||
{
|
||||
if (_editingItem == null || _mappingService == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var originalKeys = _editingItem.OriginalTriggerKeys;
|
||||
|
||||
switch (_editingItem.Type)
|
||||
{
|
||||
case EditingItem.ItemType.Remapping:
|
||||
if (_editingItem.Item is Remapping remapping)
|
||||
{
|
||||
RemappingHelper.DeleteRemapping(_mappingService, remapping);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case EditingItem.ItemType.TextMapping:
|
||||
if (originalKeys.Count == 1)
|
||||
{
|
||||
int originalKey = _mappingService.GetKeyCodeFromName(originalKeys[0]);
|
||||
if (originalKey != 0)
|
||||
{
|
||||
_mappingService.DeleteSingleKeyToTextMapping(originalKey);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string originalKeysString = string.Join(";", originalKeys.Select(k => _mappingService.GetKeyCodeFromName(k).ToString(CultureInfo.InvariantCulture)));
|
||||
_mappingService.DeleteShortcutMapping(originalKeysString, _editingItem.IsAllApps ? string.Empty : _editingItem.AppName ?? string.Empty);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case EditingItem.ItemType.ProgramShortcut:
|
||||
if (_editingItem.Item is ProgramShortcut programShortcut)
|
||||
{
|
||||
if (originalKeys.Count == 1)
|
||||
{
|
||||
int originalKey = _mappingService.GetKeyCodeFromName(originalKeys[0]);
|
||||
if (originalKey != 0)
|
||||
{
|
||||
_mappingService.DeleteSingleKeyMapping(originalKey);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string originalKeysString = string.Join(";", originalKeys.Select(k => _mappingService.GetKeyCodeFromName(k).ToString(CultureInfo.InvariantCulture)));
|
||||
_mappingService.DeleteShortcutMapping(originalKeysString);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(programShortcut.Id))
|
||||
{
|
||||
SettingsManager.RemoveShortcutKeyMappingFromSettings(programShortcut.Id);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case EditingItem.ItemType.UrlShortcut:
|
||||
if (originalKeys.Count == 1)
|
||||
{
|
||||
int originalKey = _mappingService.GetKeyCodeFromName(originalKeys[0]);
|
||||
if (originalKey != 0)
|
||||
{
|
||||
_mappingService.DeleteSingleKeyMapping(originalKey);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string originalKeysString = string.Join(";", originalKeys.Select(k => _mappingService.GetKeyCodeFromName(k).ToString(CultureInfo.InvariantCulture)));
|
||||
_mappingService.DeleteShortcutMapping(originalKeysString);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Error deleting existing mapping: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private bool SaveKeyOrShortcutMapping(List<string> triggerKeys)
|
||||
{
|
||||
List<string> actionKeys = UnifiedMappingControl.GetActionKeys();
|
||||
bool isAppSpecific = UnifiedMappingControl.GetIsAppSpecific();
|
||||
string appName = UnifiedMappingControl.GetAppName();
|
||||
|
||||
if (actionKeys == null || actionKeys.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return RemappingHelper.SaveMapping(_mappingService!, triggerKeys, actionKeys, isAppSpecific, appName);
|
||||
}
|
||||
|
||||
private bool SaveTextMapping(List<string> triggerKeys)
|
||||
{
|
||||
string textContent = UnifiedMappingControl.GetTextContent();
|
||||
bool isAppSpecific = UnifiedMappingControl.GetIsAppSpecific();
|
||||
string appName = UnifiedMappingControl.GetAppName();
|
||||
|
||||
if (string.IsNullOrEmpty(textContent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (triggerKeys.Count == 1)
|
||||
{
|
||||
// Single key to text mapping
|
||||
int originalKey = _mappingService!.GetKeyCodeFromName(triggerKeys[0]);
|
||||
if (originalKey != 0)
|
||||
{
|
||||
bool saved = _mappingService.AddSingleKeyToTextMapping(originalKey, textContent);
|
||||
if (saved)
|
||||
{
|
||||
return _mappingService.SaveSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Shortcut to text mapping
|
||||
string originalKeysString = string.Join(";", triggerKeys.Select(k => _mappingService!.GetKeyCodeFromName(k).ToString(CultureInfo.InvariantCulture)));
|
||||
|
||||
bool saved;
|
||||
if (isAppSpecific && !string.IsNullOrEmpty(appName))
|
||||
{
|
||||
saved = _mappingService!.AddShortcutMapping(originalKeysString, textContent, appName, ShortcutOperationType.RemapText);
|
||||
}
|
||||
else
|
||||
{
|
||||
saved = _mappingService!.AddShortcutMapping(originalKeysString, textContent, operationType: ShortcutOperationType.RemapText);
|
||||
}
|
||||
|
||||
if (saved)
|
||||
{
|
||||
return _mappingService.SaveSettings();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool SaveUrlMapping(List<string> triggerKeys)
|
||||
{
|
||||
string url = UnifiedMappingControl.GetUrl();
|
||||
|
||||
if (string.IsNullOrEmpty(url))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string originalKeysString = string.Join(";", triggerKeys.Select(k => _mappingService!.GetKeyCodeFromName(k).ToString(CultureInfo.InvariantCulture)));
|
||||
|
||||
ShortcutKeyMapping shortcutKeyMapping = new ShortcutKeyMapping()
|
||||
{
|
||||
OperationType = ShortcutOperationType.OpenUri,
|
||||
OriginalKeys = originalKeysString,
|
||||
TargetKeys = originalKeysString,
|
||||
UriToOpen = url,
|
||||
};
|
||||
|
||||
bool saved = _mappingService!.AddShorcutMapping(shortcutKeyMapping);
|
||||
|
||||
if (saved)
|
||||
{
|
||||
return _mappingService.SaveSettings();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool SaveProgramMapping(List<string> triggerKeys)
|
||||
{
|
||||
string programPath = UnifiedMappingControl.GetProgramPath();
|
||||
string programArgs = UnifiedMappingControl.GetProgramArgs();
|
||||
string startInDir = UnifiedMappingControl.GetStartInDirectory();
|
||||
ElevationLevel elevationLevel = UnifiedMappingControl.GetElevationLevel();
|
||||
StartWindowType visibility = UnifiedMappingControl.GetVisibility();
|
||||
ProgramAlreadyRunningAction ifRunningAction = UnifiedMappingControl.GetIfRunningAction();
|
||||
|
||||
if (string.IsNullOrEmpty(programPath))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string originalKeysString = string.Join(";", triggerKeys.Select(k => _mappingService!.GetKeyCodeFromName(k).ToString(CultureInfo.InvariantCulture)));
|
||||
|
||||
ShortcutKeyMapping shortcutKeyMapping = new ShortcutKeyMapping()
|
||||
{
|
||||
OperationType = ShortcutOperationType.RunProgram,
|
||||
OriginalKeys = originalKeysString,
|
||||
TargetKeys = originalKeysString,
|
||||
ProgramPath = programPath,
|
||||
ProgramArgs = programArgs,
|
||||
StartInDirectory = startInDir,
|
||||
IfRunningAction = ifRunningAction,
|
||||
Visibility = visibility,
|
||||
Elevation = elevationLevel,
|
||||
};
|
||||
|
||||
bool saved = _mappingService!.AddShorcutMapping(shortcutKeyMapping);
|
||||
|
||||
if (saved)
|
||||
{
|
||||
_mappingService.SaveSettings();
|
||||
SettingsManager.AddShortcutKeyMappingToSettings(shortcutKeyMapping);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Load Methods
|
||||
|
||||
private void LoadAllMappings()
|
||||
{
|
||||
LoadRemappings();
|
||||
@@ -244,6 +711,10 @@ namespace KeyboardManagerEditorUI.Pages
|
||||
return keyName.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
@@ -263,5 +734,8 @@ namespace KeyboardManagerEditorUI.Pages
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#pragma warning restore SA1124 // Do not use regions
|
||||
|
||||
Reference in New Issue
Block a user