mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-02-23 19:49:43 +01:00
apply xaml styling
This commit is contained in:
@@ -17,10 +17,10 @@
|
||||
<!-- Other app resources here -->
|
||||
|
||||
<x:Double x:Key="ContentDialogMaxWidth">960</x:Double>
|
||||
|
||||
<!-- Icons -->
|
||||
|
||||
<!-- Icons -->
|
||||
<x:String x:Key="ArrowIconData">M12.001 2C17.5238 2 22.001 6.47715 22.001 12C22.001 17.5228 17.5238 22 12.001 22C6.47813 22 2.00098 17.5228 2.00098 12C2.00098 6.47715 6.47813 2 12.001 2ZM12.7813 7.46897L12.6972 7.39635C12.4362 7.2027 12.078 7.20031 11.8146 7.38918L11.7206 7.46897L11.648 7.55308C11.4544 7.81407 11.452 8.17229 11.6409 8.43568L11.7206 8.52963L14.4403 11.2493H7.75027L7.6485 11.2561C7.31571 11.3013 7.05227 11.5647 7.00712 11.8975L7.00027 11.9993L7.00712 12.1011C7.05227 12.4339 7.31571 12.6973 7.6485 12.7424L7.75027 12.7493H14.4403L11.72 15.4697L11.6474 15.5538C11.4295 15.8474 11.4536 16.264 11.7198 16.5303C11.9861 16.7967 12.4027 16.8209 12.6964 16.6032L12.7805 16.5306L16.782 12.5306L16.8547 12.4464C17.0484 12.1854 17.0508 11.8272 16.8619 11.5638L16.7821 11.4698L12.7813 7.46897L12.6972 7.39635L12.7813 7.46897Z</x:String>
|
||||
|
||||
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="KeyboardManagerEditorUI.Controls.AppPageInputControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helper="using:KeyboardManagerEditorUI.Helpers"
|
||||
xmlns:local="using:KeyboardManagerEditorUI.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Loaded="UserControl_Loaded"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<StackPanel
|
||||
Width="360"
|
||||
Height="600"
|
||||
Orientation="Vertical"
|
||||
Spacing="8">
|
||||
<!-- Shortcut section -->
|
||||
<TextBlock
|
||||
x:Uid="AppPageInputControlShortcutTextBlock"
|
||||
Margin="0,12,0,8"
|
||||
FontWeight="SemiBold" />
|
||||
|
||||
<ToggleButton
|
||||
x:Name="ShortcutToggleBtn"
|
||||
Padding="0,24,0,24"
|
||||
HorizontalAlignment="Stretch"
|
||||
Checked="ShortcutToggleBtn_Checked"
|
||||
Style="{StaticResource CustomShortcutToggleButtonStyle}">
|
||||
<ToggleButton.Content>
|
||||
<ItemsControl x:Name="ShortcutKeys">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<controls:WrapPanel HorizontalSpacing="4" Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<local:KeyVisual Content="{Binding}" Style="{StaticResource DefaultKeyVisualStyle}" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ToggleButton.Content>
|
||||
</ToggleButton>
|
||||
|
||||
<StackPanel
|
||||
Margin="0,8,0,0"
|
||||
Orientation="Horizontal"
|
||||
Spacing="4">
|
||||
<TextBox
|
||||
x:Name="ProgramPathInput"
|
||||
x:Uid="AppPageInputControlExampleTextBox"
|
||||
Width="220" />
|
||||
<Button
|
||||
x:Name="ProgramPathSelectButton"
|
||||
x:Uid="AppPageInputControlPathSelectButton"
|
||||
Width="120"
|
||||
VerticalAlignment="Bottom"
|
||||
Click="ProgramPathSelectButton_Click" />
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock
|
||||
x:Uid="AppPageInputControlExtraOptionsTextBlock"
|
||||
Margin="0,12,0,8"
|
||||
FontWeight="SemiBold" />
|
||||
<StackPanel
|
||||
Margin="0,8,0,0"
|
||||
Orientation="Vertical"
|
||||
Spacing="8">
|
||||
<TextBox
|
||||
x:Name="ProgramArgsInput"
|
||||
x:Uid="AppPageInputControlArgumentsTextBox"
|
||||
Width="360" />
|
||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||
<TextBox
|
||||
x:Name="StartInPathInput"
|
||||
x:Uid="AppPageInputControlStartInTextBox"
|
||||
Width="220" />
|
||||
<Button
|
||||
x:Name="StartInSelectButton"
|
||||
x:Uid="AppPageInputControlStartInSelectButton"
|
||||
Width="120"
|
||||
VerticalAlignment="Bottom"
|
||||
Click="StartInSelectButton_Click" />
|
||||
</StackPanel>
|
||||
<ComboBox
|
||||
x:Name="ElevationComboBox"
|
||||
x:Uid="AppPageInputControlElevationComboBox"
|
||||
Width="360"
|
||||
SelectedValue="Normal">
|
||||
<x:String>Normal</x:String>
|
||||
<x:String>Elevated</x:String>
|
||||
<x:String>Different user</x:String>
|
||||
</ComboBox>
|
||||
<ComboBox
|
||||
x:Name="IfRunningComboBox"
|
||||
x:Uid="AppPageInputControlIfRunningComboBox"
|
||||
Width="360"
|
||||
SelectedValue="Show window">
|
||||
<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="AppPageInputControlVisibilityComboBox"
|
||||
Width="360"
|
||||
SelectedValue="Normal">
|
||||
<x:String>Normal</x:String>
|
||||
<x:String>Hidden</x:String>
|
||||
<x:String>Minimized</x:String>
|
||||
<x:String>Maximized</x:String>
|
||||
</ComboBox>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@@ -1,253 +0,0 @@
|
||||
// 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 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;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Controls
|
||||
{
|
||||
public sealed partial class AppPageInputControl : UserControl, IKeyboardHookTarget
|
||||
{
|
||||
private ObservableCollection<string> _shortcutKeys = new ObservableCollection<string>();
|
||||
private TeachingTip? currentNotification;
|
||||
private DispatcherTimer? notificationTimer;
|
||||
|
||||
// private bool _internalUpdate;
|
||||
public AppPageInputControl()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
this.ShortcutKeys.ItemsSource = _shortcutKeys;
|
||||
|
||||
ShortcutToggleBtn.IsChecked = true;
|
||||
}
|
||||
|
||||
private void UserControl_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
KeyboardHookHelper.Instance.ActivateHook(this);
|
||||
ProgramPathInput.GotFocus += ProgramInputBox_GotFocus;
|
||||
|
||||
ProgramArgsInput.GotFocus += InputArgs_GotFocus;
|
||||
}
|
||||
|
||||
private void ShortcutToggleBtn_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (ShortcutToggleBtn.IsChecked == true)
|
||||
{
|
||||
KeyboardHookHelper.Instance.ActivateHook(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnKeyDown(VirtualKey key, List<string> formattedKeys)
|
||||
{
|
||||
_shortcutKeys.Clear();
|
||||
foreach (var keyName in formattedKeys)
|
||||
{
|
||||
_shortcutKeys.Add(keyName);
|
||||
}
|
||||
}
|
||||
|
||||
private void ProgramInputBox_GotFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Clean up the keyboard hook when the text box gains focus
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
|
||||
if (ShortcutToggleBtn != null && ShortcutToggleBtn.IsChecked == true)
|
||||
{
|
||||
ShortcutToggleBtn.IsChecked = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnInputLimitReached()
|
||||
{
|
||||
ShowNotificationTip("Shortcuts can only have up to 4 modifier keys");
|
||||
}
|
||||
|
||||
private void InputArgs_GotFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// if (_internalUpdate)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
|
||||
if (ShortcutToggleBtn != null && ShortcutToggleBtn.IsChecked == true)
|
||||
{
|
||||
ShortcutToggleBtn.IsChecked = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowNotificationTip(string message)
|
||||
{
|
||||
CloseExistingNotification();
|
||||
|
||||
currentNotification = new TeachingTip
|
||||
{
|
||||
Title = "Input Limit",
|
||||
Subtitle = message,
|
||||
IsLightDismissEnabled = true,
|
||||
PreferredPlacement = TeachingTipPlacementMode.Top,
|
||||
XamlRoot = this.XamlRoot,
|
||||
IconSource = new SymbolIconSource { Symbol = Symbol.Important },
|
||||
Target = ShortcutToggleBtn,
|
||||
};
|
||||
|
||||
if (this.Content is Panel rootPanel)
|
||||
{
|
||||
rootPanel.Children.Add(currentNotification);
|
||||
currentNotification.IsOpen = true;
|
||||
|
||||
notificationTimer = new DispatcherTimer();
|
||||
notificationTimer.Interval = TimeSpan.FromMilliseconds(EditorConstants.DefaultNotificationTimeout);
|
||||
notificationTimer.Tick += (s, e) =>
|
||||
{
|
||||
CloseExistingNotification();
|
||||
};
|
||||
notificationTimer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseExistingNotification()
|
||||
{
|
||||
if (notificationTimer != null)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearKeys()
|
||||
{
|
||||
_shortcutKeys.Clear();
|
||||
}
|
||||
|
||||
public List<string> GetShortcutKeys()
|
||||
{
|
||||
List<string> keys = new List<string>();
|
||||
|
||||
foreach (var key in _shortcutKeys)
|
||||
{
|
||||
keys.Add(key);
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
public string GetProgramPathContent()
|
||||
{
|
||||
return ProgramPathInput.Text;
|
||||
}
|
||||
|
||||
public string GetProgramArgsContent()
|
||||
{
|
||||
return ProgramArgsInput.Text;
|
||||
}
|
||||
|
||||
public string GetStartInDirectory()
|
||||
{
|
||||
return StartInPathInput.Text;
|
||||
}
|
||||
|
||||
public ElevationLevel GetElevationLevel()
|
||||
{
|
||||
return (ElevationLevel)ElevationComboBox.SelectedIndex;
|
||||
}
|
||||
|
||||
public StartWindowType GetVisibility()
|
||||
{
|
||||
return (StartWindowType)VisibilityComboBox.SelectedIndex;
|
||||
}
|
||||
|
||||
public ProgramAlreadyRunningAction GetIfRunningAction()
|
||||
{
|
||||
return (ProgramAlreadyRunningAction)IfRunningComboBox.SelectedIndex;
|
||||
}
|
||||
|
||||
public void SetShortcutKeys(List<string> keys)
|
||||
{
|
||||
if (keys != null)
|
||||
{
|
||||
_shortcutKeys.Clear();
|
||||
foreach (var key in keys)
|
||||
{
|
||||
_shortcutKeys.Add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetProgramPathContent(string text)
|
||||
{
|
||||
ProgramPathInput.Text = text;
|
||||
}
|
||||
|
||||
public void SetProgramArgsContent(string text)
|
||||
{
|
||||
ProgramArgsInput.Text = text;
|
||||
}
|
||||
|
||||
private async void ProgramPathSelectButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var picker = new FileOpenPicker();
|
||||
|
||||
// Get the window handle (HWND) for the current window
|
||||
var hwnd = WindowNative.GetWindowHandle(App.MainWindow);
|
||||
InitializeWithWindow.Initialize(picker, hwnd);
|
||||
|
||||
// Set file type filter to .exe
|
||||
picker.FileTypeFilter.Add(".exe");
|
||||
|
||||
// Show the picker
|
||||
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();
|
||||
|
||||
// Get the window handle (HWND) for the current window
|
||||
var hwnd = WindowNative.GetWindowHandle(App.MainWindow);
|
||||
InitializeWithWindow.Initialize(picker, hwnd);
|
||||
|
||||
// Set file type filter (required even for folders)
|
||||
picker.FileTypeFilter.Add("*");
|
||||
|
||||
// Show the picker
|
||||
StorageFolder folder = await picker.PickSingleFolderAsync();
|
||||
|
||||
if (folder != null)
|
||||
{
|
||||
StartInPathInput.Text = folder.Path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="KeyboardManagerEditorUI.Controls.InputControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helper="using:KeyboardManagerEditorUI.Helpers"
|
||||
xmlns:local="using:KeyboardManagerEditorUI.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Loaded="UserControl_Loaded"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<StackPanel>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="240" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="240" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock x:Uid="InputControlOriginalKeysTextBlock" Margin="0,12,0,0" />
|
||||
<Grid Grid.Column="2">
|
||||
<TextBlock x:Uid="InputControlNewKeysTextBlock" Margin="0,12,0,0" />
|
||||
</Grid>
|
||||
<Grid Grid.Row="1" Margin="0,8,0,0">
|
||||
<ToggleButton
|
||||
x:Name="OriginalToggleBtn"
|
||||
MinHeight="86"
|
||||
Padding="8,24,8,24"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Center"
|
||||
VerticalContentAlignment="Center"
|
||||
Checked="OriginalToggleBtn_Checked"
|
||||
Style="{StaticResource CustomShortcutToggleButtonStyle}">
|
||||
<ToggleButton.Content>
|
||||
<ItemsControl x:Name="OriginalKeys">
|
||||
<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>
|
||||
</Grid>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="24,0,24,0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}"
|
||||
Text="" />
|
||||
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Margin="0,8,0,0">
|
||||
<ToggleButton
|
||||
x:Name="RemappedToggleBtn"
|
||||
MinHeight="86"
|
||||
Padding="8,24,8,24"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Center"
|
||||
VerticalContentAlignment="Center"
|
||||
Checked="RemappedToggleBtn_Checked"
|
||||
Style="{StaticResource CustomShortcutToggleButtonStyle}">
|
||||
<ToggleButton.Content>
|
||||
<ItemsControl x:Name="RemappedKeys">
|
||||
<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>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<CheckBox
|
||||
x:Name="AllAppsCheckBox"
|
||||
x:Uid="InputControlAllAppsCheckBox"
|
||||
Margin="0,24,0,12" />
|
||||
<TextBox
|
||||
x:Name="AppNameTextBox"
|
||||
x:Uid="InputControlAppNameTextBox"
|
||||
IsEnabled="{Binding ElementName=AllAppsCheckBox, Path=IsChecked}" />
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@@ -1,419 +0,0 @@
|
||||
// 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 KeyboardManagerEditorUI.Interop;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Windows.System;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Controls
|
||||
{
|
||||
public sealed partial class InputControl : UserControl, IDisposable, IKeyboardHookTarget
|
||||
{
|
||||
// Collection to store original and remapped keys
|
||||
private ObservableCollection<string> _originalKeys = new ObservableCollection<string>();
|
||||
private ObservableCollection<string> _remappedKeys = new ObservableCollection<string>();
|
||||
|
||||
// TeachingTip for notifications
|
||||
private TeachingTip? currentNotification;
|
||||
private DispatcherTimer? notificationTimer;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
public static readonly DependencyProperty InputModeProperty =
|
||||
DependencyProperty.Register(
|
||||
"InputMode",
|
||||
typeof(KeyInputMode),
|
||||
typeof(InputControl),
|
||||
new PropertyMetadata(KeyInputMode.OriginalKeys));
|
||||
|
||||
public KeyInputMode InputMode
|
||||
{
|
||||
get { return (KeyInputMode)GetValue(InputModeProperty); }
|
||||
set { SetValue(InputModeProperty, value); }
|
||||
}
|
||||
|
||||
public InputControl()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
this.OriginalKeys.ItemsSource = _originalKeys;
|
||||
this.RemappedKeys.ItemsSource = _remappedKeys;
|
||||
|
||||
this.Unloaded += InputControl_Unloaded;
|
||||
|
||||
// Set the default focus state
|
||||
OriginalToggleBtn.IsChecked = true;
|
||||
|
||||
// Ensure AllAppsCheckBox is in the correct state initially
|
||||
UpdateAllAppsCheckBoxState();
|
||||
}
|
||||
|
||||
private void UserControl_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
AllAppsCheckBox.Checked += AllAppsCheckBox_Checked;
|
||||
AllAppsCheckBox.Unchecked += AllAppsCheckBox_Unchecked;
|
||||
|
||||
AppNameTextBox.GotFocus += AppNameTextBox_GotFocus;
|
||||
}
|
||||
|
||||
private void InputControl_Unloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Reset the control when it is unloaded
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void OnKeyDown(VirtualKey key, List<string> formattedKeys)
|
||||
{
|
||||
if (InputMode == KeyInputMode.RemappedKeys)
|
||||
{
|
||||
_remappedKeys.Clear();
|
||||
foreach (var keyName in formattedKeys)
|
||||
{
|
||||
_remappedKeys.Add(keyName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_originalKeys.Clear();
|
||||
foreach (var keyName in formattedKeys)
|
||||
{
|
||||
_originalKeys.Add(keyName);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateAllAppsCheckBoxState();
|
||||
}
|
||||
|
||||
public void ClearKeys()
|
||||
{
|
||||
if (InputMode == KeyInputMode.RemappedKeys)
|
||||
{
|
||||
_remappedKeys.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
_originalKeys.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnInputLimitReached()
|
||||
{
|
||||
ShowNotificationTip("Shortcuts can only have up to 4 modifier keys");
|
||||
}
|
||||
|
||||
public void CleanupKeyboardHook()
|
||||
{
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
}
|
||||
|
||||
private void RemappedToggleBtn_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Only set NewMode to true if RemappedToggleBtn is checked
|
||||
if (RemappedToggleBtn.IsChecked == true)
|
||||
{
|
||||
InputMode = KeyInputMode.RemappedKeys;
|
||||
|
||||
// Make sure OriginalToggleBtn is unchecked
|
||||
if (OriginalToggleBtn.IsChecked == true)
|
||||
{
|
||||
OriginalToggleBtn.IsChecked = false;
|
||||
}
|
||||
|
||||
KeyboardHookHelper.Instance.ActivateHook(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
CleanupKeyboardHook();
|
||||
}
|
||||
}
|
||||
|
||||
private void OriginalToggleBtn_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Only set NewMode to false if OriginalToggleBtn is checked
|
||||
if (OriginalToggleBtn.IsChecked == true)
|
||||
{
|
||||
InputMode = KeyInputMode.OriginalKeys;
|
||||
|
||||
// Make sure RemappedToggleBtn is unchecked
|
||||
if (RemappedToggleBtn.IsChecked == true)
|
||||
{
|
||||
RemappedToggleBtn.IsChecked = false;
|
||||
}
|
||||
|
||||
KeyboardHookHelper.Instance.ActivateHook(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void AllAppsCheckBox_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (RemappedToggleBtn != null && RemappedToggleBtn.IsChecked == true)
|
||||
{
|
||||
RemappedToggleBtn.IsChecked = false;
|
||||
}
|
||||
|
||||
if (OriginalToggleBtn != null && OriginalToggleBtn.IsChecked == true)
|
||||
{
|
||||
OriginalToggleBtn.IsChecked = false;
|
||||
}
|
||||
|
||||
CleanupKeyboardHook();
|
||||
|
||||
AppNameTextBox.Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
private void AllAppsCheckBox_Unchecked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
AppNameTextBox.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private void AppNameTextBox_GotFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Reset the focus state when the AppNameTextBox is focused
|
||||
if (RemappedToggleBtn != null && RemappedToggleBtn.IsChecked == true)
|
||||
{
|
||||
RemappedToggleBtn.IsChecked = false;
|
||||
}
|
||||
|
||||
if (OriginalToggleBtn != null && OriginalToggleBtn.IsChecked == true)
|
||||
{
|
||||
OriginalToggleBtn.IsChecked = false;
|
||||
}
|
||||
|
||||
CleanupKeyboardHook();
|
||||
}
|
||||
|
||||
public void SetRemappedKeys(List<string> keys)
|
||||
{
|
||||
_remappedKeys.Clear();
|
||||
if (keys != null)
|
||||
{
|
||||
foreach (var key in keys)
|
||||
{
|
||||
_remappedKeys.Add(key);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateAllAppsCheckBoxState();
|
||||
}
|
||||
|
||||
public void SetOriginalKeys(List<string> keys)
|
||||
{
|
||||
_originalKeys.Clear();
|
||||
if (keys != null)
|
||||
{
|
||||
foreach (var key in keys)
|
||||
{
|
||||
_originalKeys.Add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetApp(bool isSpecificApp, string appName)
|
||||
{
|
||||
if (isSpecificApp)
|
||||
{
|
||||
AllAppsCheckBox.IsChecked = true;
|
||||
AppNameTextBox.Text = appName;
|
||||
AppNameTextBox.Visibility = Visibility.Visible;
|
||||
}
|
||||
else
|
||||
{
|
||||
AllAppsCheckBox.IsChecked = false;
|
||||
AppNameTextBox.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
public List<string> GetOriginalKeys()
|
||||
{
|
||||
return _originalKeys.ToList();
|
||||
}
|
||||
|
||||
public List<string> GetRemappedKeys()
|
||||
{
|
||||
return _remappedKeys.ToList();
|
||||
}
|
||||
|
||||
public bool GetIsAppSpecific()
|
||||
{
|
||||
return AllAppsCheckBox.IsChecked ?? false;
|
||||
}
|
||||
|
||||
public string GetAppName()
|
||||
{
|
||||
return AppNameTextBox.Text ?? string.Empty;
|
||||
}
|
||||
|
||||
public void SetUpToggleButtonInitialStatus()
|
||||
{
|
||||
// Ensure OriginalToggleBtn is checked
|
||||
if (OriginalToggleBtn != null && OriginalToggleBtn.IsChecked != true)
|
||||
{
|
||||
OriginalToggleBtn.IsChecked = true;
|
||||
}
|
||||
|
||||
// Make sure RemappedToggleBtn is not checked
|
||||
if (RemappedToggleBtn != null && RemappedToggleBtn.IsChecked == true)
|
||||
{
|
||||
RemappedToggleBtn.IsChecked = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateAllAppsCheckBoxState()
|
||||
{
|
||||
// Only enable app-specific remapping for shortcuts (multiple keys)
|
||||
bool isShortcut = _originalKeys.Count > 1;
|
||||
|
||||
AllAppsCheckBox.IsEnabled = isShortcut;
|
||||
|
||||
// If it's not a shortcut, ensure the checkbox is unchecked and app textbox is hidden
|
||||
if (!isShortcut)
|
||||
{
|
||||
AllAppsCheckBox.IsChecked = false;
|
||||
AppNameTextBox.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowNotificationTip(string message)
|
||||
{
|
||||
// If there's already an active notification, close and remove it first
|
||||
CloseExistingNotification();
|
||||
|
||||
// Create a new notification
|
||||
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 toggle button that triggered the notification
|
||||
currentNotification.Target = InputMode == KeyInputMode.RemappedKeys ? RemappedToggleBtn : OriginalToggleBtn;
|
||||
|
||||
// Add the notification to the root panel and show it
|
||||
if (this.Content is Panel rootPanel)
|
||||
{
|
||||
rootPanel.Children.Add(currentNotification);
|
||||
currentNotification.IsOpen = true;
|
||||
|
||||
// Create a timer to auto-dismiss the notification
|
||||
notificationTimer = new DispatcherTimer();
|
||||
notificationTimer.Interval = TimeSpan.FromMilliseconds(EditorConstants.DefaultNotificationTimeout);
|
||||
notificationTimer.Tick += (s, e) =>
|
||||
{
|
||||
CloseExistingNotification();
|
||||
notificationTimer = null;
|
||||
};
|
||||
notificationTimer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
// Helper method to close existing notifications
|
||||
private void CloseExistingNotification()
|
||||
{
|
||||
// Stop any running timer
|
||||
if (notificationTimer != null)
|
||||
{
|
||||
notificationTimer.Stop();
|
||||
notificationTimer = null;
|
||||
}
|
||||
|
||||
// Close and remove any existing notification
|
||||
if (currentNotification != null && currentNotification.IsOpen)
|
||||
{
|
||||
currentNotification.IsOpen = false;
|
||||
|
||||
if (this.Content is Panel rootPanel)
|
||||
{
|
||||
rootPanel.Children.Remove(currentNotification);
|
||||
}
|
||||
|
||||
currentNotification = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetToggleButtons()
|
||||
{
|
||||
// Reset toggle button status without clearing the key displays
|
||||
if (RemappedToggleBtn != null)
|
||||
{
|
||||
RemappedToggleBtn.IsChecked = false;
|
||||
}
|
||||
|
||||
if (OriginalToggleBtn != null)
|
||||
{
|
||||
OriginalToggleBtn.IsChecked = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
// Reset displayed keys
|
||||
_originalKeys.Clear();
|
||||
_remappedKeys.Clear();
|
||||
|
||||
// Reset toggle button status
|
||||
if (RemappedToggleBtn != null)
|
||||
{
|
||||
RemappedToggleBtn.IsChecked = false;
|
||||
}
|
||||
|
||||
if (OriginalToggleBtn != null)
|
||||
{
|
||||
OriginalToggleBtn.IsChecked = false;
|
||||
}
|
||||
|
||||
InputMode = KeyInputMode.OriginalKeys;
|
||||
|
||||
// Reset app name text box
|
||||
if (AppNameTextBox != null)
|
||||
{
|
||||
AppNameTextBox.Text = string.Empty;
|
||||
}
|
||||
|
||||
UpdateAllAppsCheckBoxState();
|
||||
|
||||
// Close any existing notifications
|
||||
CloseExistingNotification();
|
||||
|
||||
// Reset the focus status
|
||||
if (this.FocusState != FocusState.Unfocused)
|
||||
{
|
||||
this.IsTabStop = false;
|
||||
this.IsTabStop = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
CleanupKeyboardHook();
|
||||
CloseExistingNotification();
|
||||
Reset();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="KeyboardManagerEditorUI.Controls.TextPageInputControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helper="using:KeyboardManagerEditorUI.Helpers"
|
||||
xmlns:local="using:KeyboardManagerEditorUI.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Loaded="UserControl_Loaded"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<StackPanel
|
||||
Width="360"
|
||||
Height="360"
|
||||
Orientation="Vertical">
|
||||
<!-- Shortcut section -->
|
||||
<TextBlock
|
||||
x:Uid="TextPageInputControlShortcutKeysTextBlock"
|
||||
Margin="0,12,0,8"
|
||||
FontWeight="SemiBold" />
|
||||
|
||||
<ToggleButton
|
||||
x:Name="ShortcutToggleBtn"
|
||||
Padding="0,24,0,24"
|
||||
HorizontalAlignment="Stretch"
|
||||
Checked="ShortcutToggleBtn_Checked"
|
||||
Style="{StaticResource CustomShortcutToggleButtonStyle}">
|
||||
<ToggleButton.Content>
|
||||
<ItemsControl x:Name="ShortcutKeys">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<controls:WrapPanel HorizontalSpacing="4" Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<local:KeyVisual Content="{Binding}" Style="{StaticResource DefaultKeyVisualStyle}" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ToggleButton.Content>
|
||||
</ToggleButton>
|
||||
|
||||
<!-- Text section -->
|
||||
<TextBox
|
||||
x:Name="TextContentBox"
|
||||
x:Uid="TextPageInputControlTextContentTextBox"
|
||||
Height="120"
|
||||
Margin="0,8,0,0"
|
||||
AcceptsReturn="True"
|
||||
Background="{ThemeResource TextControlBackgroundFocused}"
|
||||
BorderBrush="{ThemeResource ControlStrokeColorDefaultBrush}"
|
||||
FontSize="13"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<!-- App specific section -->
|
||||
<CheckBox
|
||||
x:Name="AllAppsCheckBox"
|
||||
x:Uid="TextPageInputControlAllAppsCheckBox"
|
||||
Margin="0,8,0,0"
|
||||
Foreground="{ThemeResource TextFillColorPrimaryBrush}" />
|
||||
|
||||
<TextBox
|
||||
x:Name="AppNameTextBox"
|
||||
x:Uid="TextPageInputControlAllAppsTextBox"
|
||||
Background="{ThemeResource TextControlBackgroundFocused}"
|
||||
BorderBrush="{ThemeResource ControlStrokeColorDefaultBrush}"
|
||||
IsEnabled="{Binding ElementName=AllAppsCheckBox, Path=IsChecked}" />
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@@ -1,252 +0,0 @@
|
||||
// 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 KeyboardManagerEditorUI.Helpers;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Windows.System;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Controls
|
||||
{
|
||||
public sealed partial class TextPageInputControl : UserControl, IKeyboardHookTarget
|
||||
{
|
||||
private ObservableCollection<string> _shortcutKeys = new ObservableCollection<string>();
|
||||
private TeachingTip? currentNotification;
|
||||
private DispatcherTimer? notificationTimer;
|
||||
private bool _internalUpdate;
|
||||
|
||||
public TextPageInputControl()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
this.ShortcutKeys.ItemsSource = _shortcutKeys;
|
||||
|
||||
ShortcutToggleBtn.IsChecked = true;
|
||||
}
|
||||
|
||||
private void UserControl_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
KeyboardHookHelper.Instance.ActivateHook(this);
|
||||
TextContentBox.GotFocus += TextContentBox_GotFocus;
|
||||
|
||||
AllAppsCheckBox.Checked += AllAppsCheckBox_Changed;
|
||||
AllAppsCheckBox.Unchecked += AllAppsCheckBox_Changed;
|
||||
AppNameTextBox.GotFocus += AppNameTextBox_GotFocus;
|
||||
|
||||
AppNameTextBox.Visibility = AllAppsCheckBox.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private void ShortcutToggleBtn_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (ShortcutToggleBtn.IsChecked == true)
|
||||
{
|
||||
KeyboardHookHelper.Instance.ActivateHook(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnKeyDown(VirtualKey key, List<string> formattedKeys)
|
||||
{
|
||||
_shortcutKeys.Clear();
|
||||
foreach (var keyName in formattedKeys)
|
||||
{
|
||||
_shortcutKeys.Add(keyName);
|
||||
}
|
||||
|
||||
UpdateAllAppsCheckBoxState();
|
||||
}
|
||||
|
||||
private void TextContentBox_GotFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Clean up the keyboard hook when the text box gains focus
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
|
||||
if (ShortcutToggleBtn != null && ShortcutToggleBtn.IsChecked == true)
|
||||
{
|
||||
ShortcutToggleBtn.IsChecked = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnInputLimitReached()
|
||||
{
|
||||
ShowNotificationTip("Shortcuts can only have up to 4 modifier keys");
|
||||
}
|
||||
|
||||
public void UpdateAllAppsCheckBoxState()
|
||||
{
|
||||
// Only enable app-specific remapping for shortcuts (multiple keys)
|
||||
bool isShortcut = _shortcutKeys.Count > 1;
|
||||
|
||||
AllAppsCheckBox.IsEnabled = isShortcut;
|
||||
|
||||
// If it's not a shortcut, ensure the checkbox is unchecked and app textbox is hidden
|
||||
try
|
||||
{
|
||||
if (!isShortcut)
|
||||
{
|
||||
_internalUpdate = true;
|
||||
AllAppsCheckBox.IsChecked = false;
|
||||
AppNameTextBox.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
else if (AllAppsCheckBox.IsChecked == true)
|
||||
{
|
||||
AppNameTextBox.Visibility = Visibility.Visible;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_internalUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void AllAppsCheckBox_Changed(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_internalUpdate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
|
||||
if (ShortcutToggleBtn != null && ShortcutToggleBtn.IsChecked == true)
|
||||
{
|
||||
ShortcutToggleBtn.IsChecked = false;
|
||||
}
|
||||
|
||||
AppNameTextBox.Visibility = AllAppsCheckBox.IsChecked == true ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private void AppNameTextBox_GotFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_internalUpdate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
|
||||
if (ShortcutToggleBtn != null && ShortcutToggleBtn.IsChecked == true)
|
||||
{
|
||||
ShortcutToggleBtn.IsChecked = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void ShowNotificationTip(string message)
|
||||
{
|
||||
CloseExistingNotification();
|
||||
|
||||
currentNotification = new TeachingTip
|
||||
{
|
||||
Title = "Input Limit",
|
||||
Subtitle = message,
|
||||
IsLightDismissEnabled = true,
|
||||
PreferredPlacement = TeachingTipPlacementMode.Top,
|
||||
XamlRoot = this.XamlRoot,
|
||||
IconSource = new SymbolIconSource { Symbol = Symbol.Important },
|
||||
Target = ShortcutToggleBtn,
|
||||
};
|
||||
|
||||
if (this.Content is Panel rootPanel)
|
||||
{
|
||||
rootPanel.Children.Add(currentNotification);
|
||||
currentNotification.IsOpen = true;
|
||||
|
||||
notificationTimer = new DispatcherTimer();
|
||||
notificationTimer.Interval = TimeSpan.FromMilliseconds(EditorConstants.DefaultNotificationTimeout);
|
||||
notificationTimer.Tick += (s, e) =>
|
||||
{
|
||||
CloseExistingNotification();
|
||||
};
|
||||
notificationTimer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseExistingNotification()
|
||||
{
|
||||
if (notificationTimer != null)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearKeys()
|
||||
{
|
||||
_shortcutKeys.Clear();
|
||||
UpdateAllAppsCheckBoxState();
|
||||
}
|
||||
|
||||
public List<string> GetShortcutKeys()
|
||||
{
|
||||
List<string> keys = new List<string>();
|
||||
|
||||
foreach (var key in _shortcutKeys)
|
||||
{
|
||||
keys.Add(key);
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
public string GetTextContent()
|
||||
{
|
||||
return TextContentBox.Text;
|
||||
}
|
||||
|
||||
public bool GetIsAppSpecific()
|
||||
{
|
||||
return AllAppsCheckBox.IsChecked ?? false;
|
||||
}
|
||||
|
||||
public string GetAppName()
|
||||
{
|
||||
return AllAppsCheckBox.IsChecked == true ? AppNameTextBox.Text : string.Empty;
|
||||
}
|
||||
|
||||
public void SetShortcutKeys(List<string> keys)
|
||||
{
|
||||
if (keys != null)
|
||||
{
|
||||
_shortcutKeys.Clear();
|
||||
foreach (var key in keys)
|
||||
{
|
||||
_shortcutKeys.Add(key);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateAllAppsCheckBoxState();
|
||||
}
|
||||
|
||||
public void SetTextContent(string text)
|
||||
{
|
||||
TextContentBox.Text = text;
|
||||
}
|
||||
|
||||
public void SetAppSpecific(bool isAppSpecific, string appName)
|
||||
{
|
||||
AllAppsCheckBox.IsChecked = isAppSpecific;
|
||||
if (isAppSpecific)
|
||||
{
|
||||
AppNameTextBox.Text = appName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="KeyboardManagerEditorUI.Controls.UrlPageInputControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helper="using:KeyboardManagerEditorUI.Helpers"
|
||||
xmlns:local="using:KeyboardManagerEditorUI.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Loaded="UserControl_Loaded"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<StackPanel
|
||||
Width="360"
|
||||
Height="360"
|
||||
Orientation="Vertical"
|
||||
Spacing="8">
|
||||
<!-- Shortcut section -->
|
||||
<TextBlock
|
||||
x:Uid="UrlPageInputControlShortcutTextBlock"
|
||||
Margin="0,12,0,8"
|
||||
FontWeight="SemiBold" />
|
||||
|
||||
<ToggleButton
|
||||
x:Name="ShortcutToggleBtn"
|
||||
Padding="0,24,0,24"
|
||||
HorizontalAlignment="Stretch"
|
||||
Checked="ShortcutToggleBtn_Checked"
|
||||
Style="{StaticResource CustomShortcutToggleButtonStyle}">
|
||||
<ToggleButton.Content>
|
||||
<ItemsControl x:Name="ShortcutKeys">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<controls:WrapPanel HorizontalSpacing="4" Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<local:KeyVisual Content="{Binding}" Style="{StaticResource DefaultKeyVisualStyle}" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</ToggleButton.Content>
|
||||
</ToggleButton>
|
||||
|
||||
<TextBox
|
||||
x:Name="UrlPathInput"
|
||||
x:Uid="UrlPageInputControlToOpenTextBox"
|
||||
Margin="0,8,0,0"
|
||||
AcceptsReturn="True"
|
||||
Background="{ThemeResource TextControlBackgroundFocused}"
|
||||
BorderBrush="{ThemeResource ControlStrokeColorDefaultBrush}"
|
||||
FontSize="13"
|
||||
TextWrapping="Wrap"
|
||||
Width="360" />
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@@ -1,167 +0,0 @@
|
||||
// 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 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;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Controls
|
||||
{
|
||||
public sealed partial class UrlPageInputControl : UserControl, IKeyboardHookTarget
|
||||
{
|
||||
private ObservableCollection<string> _shortcutKeys = new ObservableCollection<string>();
|
||||
private TeachingTip? currentNotification;
|
||||
private DispatcherTimer? notificationTimer;
|
||||
|
||||
// private bool _internalUpdate;
|
||||
public UrlPageInputControl()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
this.ShortcutKeys.ItemsSource = _shortcutKeys;
|
||||
|
||||
ShortcutToggleBtn.IsChecked = true;
|
||||
}
|
||||
|
||||
private void UserControl_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
KeyboardHookHelper.Instance.ActivateHook(this);
|
||||
UrlPathInput.GotFocus += UrlInputBox_GotFocus;
|
||||
}
|
||||
|
||||
private void ShortcutToggleBtn_Checked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (ShortcutToggleBtn.IsChecked == true)
|
||||
{
|
||||
KeyboardHookHelper.Instance.ActivateHook(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnKeyDown(VirtualKey key, List<string> formattedKeys)
|
||||
{
|
||||
_shortcutKeys.Clear();
|
||||
foreach (var keyName in formattedKeys)
|
||||
{
|
||||
_shortcutKeys.Add(keyName);
|
||||
}
|
||||
}
|
||||
|
||||
private void UrlInputBox_GotFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Clean up the keyboard hook when the text box gains focus
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
|
||||
if (ShortcutToggleBtn != null && ShortcutToggleBtn.IsChecked == true)
|
||||
{
|
||||
ShortcutToggleBtn.IsChecked = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnInputLimitReached()
|
||||
{
|
||||
ShowNotificationTip("Shortcuts can only have up to 4 modifier keys");
|
||||
}
|
||||
|
||||
public void ShowNotificationTip(string message)
|
||||
{
|
||||
CloseExistingNotification();
|
||||
|
||||
currentNotification = new TeachingTip
|
||||
{
|
||||
Title = "Input Limit",
|
||||
Subtitle = message,
|
||||
IsLightDismissEnabled = true,
|
||||
PreferredPlacement = TeachingTipPlacementMode.Top,
|
||||
XamlRoot = this.XamlRoot,
|
||||
IconSource = new SymbolIconSource { Symbol = Symbol.Important },
|
||||
Target = ShortcutToggleBtn,
|
||||
};
|
||||
|
||||
if (this.Content is Panel rootPanel)
|
||||
{
|
||||
rootPanel.Children.Add(currentNotification);
|
||||
currentNotification.IsOpen = true;
|
||||
|
||||
notificationTimer = new DispatcherTimer();
|
||||
notificationTimer.Interval = TimeSpan.FromMilliseconds(EditorConstants.DefaultNotificationTimeout);
|
||||
notificationTimer.Tick += (s, e) =>
|
||||
{
|
||||
CloseExistingNotification();
|
||||
};
|
||||
notificationTimer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseExistingNotification()
|
||||
{
|
||||
if (notificationTimer != null)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearKeys()
|
||||
{
|
||||
_shortcutKeys.Clear();
|
||||
}
|
||||
|
||||
public List<string> GetShortcutKeys()
|
||||
{
|
||||
List<string> keys = new List<string>();
|
||||
|
||||
foreach (var key in _shortcutKeys)
|
||||
{
|
||||
keys.Add(key);
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
public string GetUrlPathContent()
|
||||
{
|
||||
return UrlPathInput.Text;
|
||||
}
|
||||
|
||||
public void SetShortcutKeys(List<string> keys)
|
||||
{
|
||||
if (keys != null)
|
||||
{
|
||||
_shortcutKeys.Clear();
|
||||
foreach (var key in keys)
|
||||
{
|
||||
_shortcutKeys.Add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetUrlPathContent(string text)
|
||||
{
|
||||
UrlPathInput.Text = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,9 +27,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\Keyboard.ico" />
|
||||
<None Remove="Pages\Programs.xaml" />
|
||||
<None Remove="Pages\Text.xaml" />
|
||||
<None Remove="Pages\URLs.xaml" />
|
||||
<None Remove="Styles\Colors.xaml" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -74,33 +71,10 @@
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\UrlPageInputControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Update="Styles\Button.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Pages\URLs.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Pages\Text.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Pages\Programs.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Pages\Remappings.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<!--
|
||||
Defining the "HasPackageAndPublishMenuAddedByProject" property here allows the Solution
|
||||
|
||||
@@ -39,45 +39,5 @@
|
||||
Grid.Row="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch" />
|
||||
<!--<NavigationView
|
||||
x:Name="RootView"
|
||||
Grid.Row="1"
|
||||
IsBackButtonVisible="Collapsed"
|
||||
IsBackEnabled="False"
|
||||
IsPaneToggleButtonVisible="False"
|
||||
IsSettingsVisible="False"
|
||||
PaneDisplayMode="Top"
|
||||
SelectionChanged="RootView_SelectionChanged">
|
||||
<NavigationView.MenuItems>
|
||||
<NavigationViewItem Content="All" Tag="All">
|
||||
<NavigationViewItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
<NavigationViewItem Content="Remappings" Tag="Remappings">
|
||||
<NavigationViewItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
<NavigationViewItem Content="Text" Tag="Text">
|
||||
<NavigationViewItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
<NavigationViewItem Content="Programs" Tag="Programs">
|
||||
<NavigationViewItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
<NavigationViewItem Content="URLs" Tag="URLs">
|
||||
<NavigationViewItem.Icon>
|
||||
<FontIcon Glyph="" />
|
||||
</NavigationViewItem.Icon>
|
||||
</NavigationViewItem>
|
||||
</NavigationView.MenuItems>
|
||||
<NavigationView.Content>
|
||||
<Frame x:Name="NavigationFrame" Margin="0,0,0,0" />
|
||||
</NavigationView.Content>
|
||||
</NavigationView>-->
|
||||
</Grid>
|
||||
</winuiex:WindowEx>
|
||||
@@ -61,23 +61,5 @@ namespace KeyboardManagerEditorUI
|
||||
this.Activated -= MainWindow_Activated;
|
||||
this.Closed -= MainWindow_Closed;
|
||||
}
|
||||
|
||||
private void RootView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
|
||||
{
|
||||
// Cleanup the keyboard hook when the selected page changes
|
||||
/* KeyboardHookHelper.Instance.CleanupHook();
|
||||
|
||||
if (args.SelectedItem is NavigationViewItem selectedItem)
|
||||
{
|
||||
switch ((string)selectedItem.Tag)
|
||||
{
|
||||
case "All": NavigationFrame.Navigate(typeof(Pages.All)); break;
|
||||
case "Remappings": NavigationFrame.Navigate(typeof(Pages.Remappings)); break;
|
||||
case "Programs": NavigationFrame.Navigate(typeof(Pages.Programs)); break;
|
||||
case "Text": NavigationFrame.Navigate(typeof(Pages.Text)); break;
|
||||
case "URLs": NavigationFrame.Navigate(typeof(Pages.URLs)); break;
|
||||
}
|
||||
} */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Page
|
||||
x:Class="KeyboardManagerEditorUI.Pages.Programs"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:KeyboardManagerEditorUI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helper="using:KeyboardManagerEditorUI.Helpers"
|
||||
xmlns:local="using:KeyboardManagerEditorUI.Pages"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Padding="16">
|
||||
<StackPanel Orientation="Vertical" Spacing="12">
|
||||
<TextBlock
|
||||
x:Uid="ProgramsPageInstructionTextBlock"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="Wrap" />
|
||||
<Button
|
||||
x:Name="NewShortcutBtn"
|
||||
Height="36"
|
||||
Margin="0,12,0,0"
|
||||
Click="NewShortcutBtn_Click">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<FontIcon
|
||||
FontSize="14"
|
||||
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
|
||||
Glyph="" />
|
||||
<TextBlock
|
||||
x:Uid="ProgramsPageNewTextBlock"
|
||||
VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Grid
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="{ThemeResource LayerFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="{StaticResource OverlayCornerRadius}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="48" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="348" />
|
||||
<ColumnDefinition Width="236" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="84" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
x:Uid="ProgramsPageShortcutTextBlock"
|
||||
Grid.Column="0"
|
||||
Margin="16,-2,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<AppBarSeparator
|
||||
Grid.Column="1"
|
||||
Margin="-6,4,0,4"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
x:Uid="ProgramsPageProgramsTextBlock"
|
||||
Grid.Column="1"
|
||||
Margin="12,-2,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<Rectangle
|
||||
Grid.ColumnSpan="4"
|
||||
Height="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Bottom"
|
||||
Fill="{ThemeResource CardStrokeColorDefaultBrush}" />
|
||||
<ListView
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="4"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="ListView_ItemClick"
|
||||
ItemsSource="{x:Bind Shortcuts}"
|
||||
SelectionMode="None">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="helper:ProgramShortcut">
|
||||
<Grid Height="48">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="348" />
|
||||
<ColumnDefinition Width="476" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="84" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle
|
||||
Grid.ColumnSpan="5"
|
||||
Height="1"
|
||||
Margin="-16,0,-16,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Bottom"
|
||||
Fill="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
Opacity="0.8" />
|
||||
<ItemsControl
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
ItemsSource="{x:Bind Shortcut}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal" Spacing="4" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<controls:KeyVisual
|
||||
HorizontalAlignment="Left"
|
||||
Content="{Binding}"
|
||||
Style="{StaticResource DefaultKeyVisualStyle}" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
Spacing="8">
|
||||
<TextBlock
|
||||
VerticalAlignment="Center"
|
||||
Text="{x:Bind AppToRun}" />
|
||||
<FontIcon
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
|
||||
Glyph="">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock>
|
||||
<Run
|
||||
x:Uid="ProgramsPageArgumentsRun"
|
||||
FontWeight="SemiBold" />
|
||||
<Run Text="{x:Bind Args}" />
|
||||
</TextBlock>
|
||||
</ToolTipService.ToolTip>
|
||||
</FontIcon>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Spacing="8"
|
||||
Grid.ColumnSpan="4"
|
||||
Margin="0,0,4,0"
|
||||
Padding="8,4"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center">
|
||||
<ToggleSwitch
|
||||
IsOn="{x:Bind IsActive}"
|
||||
Toggled="ToggleSwitch_Toggled"/>
|
||||
<Button
|
||||
x:Uid="ProgramsPageDeleteButton"
|
||||
AutomationProperties.Name="Delete program shortcut"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
Click="DeleteButton_Click">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||
FontSize="16"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Glyph="" />
|
||||
</Button>
|
||||
|
||||
</StackPanel>
|
||||
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<ContentDialog
|
||||
x:Name="KeyDialog"
|
||||
x:Uid="ProgramsPageKeyDialog"
|
||||
Width="480"
|
||||
MinWidth="600"
|
||||
MaxWidth="600"
|
||||
MinHeight="600"
|
||||
MaxHeight="600"
|
||||
Height="600"
|
||||
PrimaryButtonClick="KeyDialog_PrimaryButtonClick"
|
||||
PrimaryButtonStyle="{StaticResource AccentButtonStyle}">
|
||||
<Grid>
|
||||
<controls:AppPageInputControl x:Name="AppShortcutControl" />
|
||||
</Grid>
|
||||
</ContentDialog>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -1,336 +0,0 @@
|
||||
// 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.Globalization;
|
||||
using System.Linq;
|
||||
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
|
||||
{
|
||||
public sealed partial class Programs : Page, IDisposable
|
||||
{
|
||||
private KeyboardMappingService? _mappingService;
|
||||
|
||||
// Flag to indicate if the user is editing an existing mapping
|
||||
private bool _isEditMode;
|
||||
private ProgramShortcut? _editingMapping;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
// The list of text mappings
|
||||
public ObservableCollection<ProgramShortcut> Shortcuts { get; set; } = new ObservableCollection<ProgramShortcut> { };
|
||||
|
||||
public Programs()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
try
|
||||
{
|
||||
_mappingService = new KeyboardMappingService();
|
||||
LoadProgramShortcuts();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Failed to initialize KeyboardMappingService: " + ex.Message);
|
||||
}
|
||||
|
||||
this.Unloaded += Text_Unloaded;
|
||||
}
|
||||
|
||||
private void Text_Unloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
private void LoadProgramShortcuts()
|
||||
{
|
||||
if (_mappingService == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Shortcuts.Clear();
|
||||
|
||||
foreach (var shortcutSettings in SettingsManager.GetShortcutSettingsByOperationType(ShortcutOperationType.RunProgram))
|
||||
{
|
||||
ShortcutKeyMapping mapping = shortcutSettings.Shortcut;
|
||||
string[] originalKeyCodes = mapping.OriginalKeys.Split(';');
|
||||
var originalKeyNames = new List<string>();
|
||||
foreach (var keyCode in originalKeyCodes)
|
||||
{
|
||||
if (int.TryParse(keyCode, out int code))
|
||||
{
|
||||
originalKeyNames.Add(_mappingService.GetKeyDisplayName(code));
|
||||
}
|
||||
}
|
||||
|
||||
Shortcuts.Add(new ProgramShortcut
|
||||
{
|
||||
Shortcut = originalKeyNames,
|
||||
AppToRun = mapping.ProgramPath,
|
||||
Args = mapping.ProgramArgs,
|
||||
IsActive = shortcutSettings.IsActive,
|
||||
Id = shortcutSettings.Id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async void NewShortcutBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_isEditMode = false;
|
||||
_editingMapping = null;
|
||||
|
||||
AppShortcutControl.ClearKeys();
|
||||
AppShortcutControl.SetProgramPathContent(string.Empty);
|
||||
AppShortcutControl.SetProgramArgsContent(string.Empty);
|
||||
|
||||
await KeyDialog.ShowAsync();
|
||||
}
|
||||
|
||||
private async void ListView_ItemClick(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
if (e.ClickedItem is ProgramShortcut selectedMapping)
|
||||
{
|
||||
_isEditMode = true;
|
||||
_editingMapping = selectedMapping;
|
||||
|
||||
AppShortcutControl.SetShortcutKeys(selectedMapping.Shortcut);
|
||||
AppShortcutControl.SetProgramPathContent(selectedMapping.AppToRun);
|
||||
AppShortcutControl.SetProgramArgsContent(selectedMapping.Args);
|
||||
|
||||
await KeyDialog.ShowAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private void KeyDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
if (_mappingService == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<string> keys = AppShortcutControl.GetShortcutKeys();
|
||||
string programPath = AppShortcutControl.GetProgramPathContent();
|
||||
string programArgs = AppShortcutControl.GetProgramArgsContent();
|
||||
ElevationLevel elevationLevel = AppShortcutControl.GetElevationLevel();
|
||||
StartWindowType startWindowType = AppShortcutControl.GetVisibility();
|
||||
ProgramAlreadyRunningAction programAlreadyRunningAction = AppShortcutControl.GetIfRunningAction();
|
||||
|
||||
// Validate inputs
|
||||
ValidationErrorType errorType = ValidationHelper.ValidateProgramOrUrlMapping(keys, false, string.Empty, _mappingService);
|
||||
|
||||
if (errorType != ValidationErrorType.NoError)
|
||||
{
|
||||
ShowValidationError(errorType, args);
|
||||
return;
|
||||
}
|
||||
|
||||
bool saved = false;
|
||||
|
||||
try
|
||||
{
|
||||
// Delete existing mapping if in edit mode
|
||||
if (_isEditMode && _editingMapping != null)
|
||||
{
|
||||
if (_editingMapping.Shortcut.Count == 1)
|
||||
{
|
||||
int originalKey = _mappingService.GetKeyCodeFromName(_editingMapping.Shortcut[0]);
|
||||
if (originalKey != 0)
|
||||
{
|
||||
_mappingService.DeleteSingleKeyMapping(originalKey);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string originalKeys = string.Join(";", _editingMapping.Shortcut.Select(k => _mappingService.GetKeyCodeFromName(k).ToString(CultureInfo.InvariantCulture)));
|
||||
_mappingService.DeleteShortcutMapping(originalKeys);
|
||||
}
|
||||
|
||||
SettingsManager.RemoveShortcutKeyMappingFromSettings(_editingMapping.Id);
|
||||
}
|
||||
|
||||
// Shortcut to text mapping
|
||||
string originalKeysString = string.Join(";", keys.Select(k => _mappingService.GetKeyCodeFromName(k).ToString(CultureInfo.InvariantCulture)));
|
||||
|
||||
// if (isAppSpecific && !string.IsNullOrEmpty(appName))
|
||||
// {
|
||||
// saved = _mappingService.AddShortcutMapping(originalKeysString, programPath, appName, ShortcutOperationType.RemapText);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
ShortcutKeyMapping shortcutKeyMapping = new ShortcutKeyMapping()
|
||||
{
|
||||
OperationType = ShortcutOperationType.RunProgram,
|
||||
OriginalKeys = originalKeysString,
|
||||
TargetKeys = originalKeysString,
|
||||
ProgramPath = programPath,
|
||||
ProgramArgs = programArgs,
|
||||
IfRunningAction = programAlreadyRunningAction,
|
||||
Visibility = startWindowType,
|
||||
Elevation = elevationLevel,
|
||||
};
|
||||
|
||||
saved = _mappingService.AddShorcutMapping(shortcutKeyMapping);
|
||||
|
||||
if (saved)
|
||||
{
|
||||
_mappingService.SaveSettings();
|
||||
SettingsManager.AddShortcutKeyMappingToSettings(shortcutKeyMapping);
|
||||
LoadProgramShortcuts(); // Refresh the list
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Error saving text mapping: " + ex.Message);
|
||||
args.Cancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_mappingService == null || !(sender is Button button) || !(button.DataContext is ProgramShortcut shortcut))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
bool deleted = false;
|
||||
if (shortcut.Shortcut.Count == 1)
|
||||
{
|
||||
// Single key mapping
|
||||
int originalKey = _mappingService.GetKeyCodeFromName(shortcut.Shortcut[0]);
|
||||
if (originalKey != 0)
|
||||
{
|
||||
deleted = _mappingService.DeleteSingleKeyToTextMapping(originalKey);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Shortcut mapping
|
||||
string originalKeys = string.Join(";", shortcut.Shortcut.Select(k => _mappingService.GetKeyCodeFromName(k)));
|
||||
deleted = _mappingService.DeleteShortcutMapping(originalKeys);
|
||||
}
|
||||
|
||||
Shortcuts.Remove(shortcut);
|
||||
SettingsManager.RemoveShortcutKeyMappingFromSettings(shortcut.Id);
|
||||
|
||||
if (deleted)
|
||||
{
|
||||
_mappingService.SaveSettings();
|
||||
}
|
||||
|
||||
LoadProgramShortcuts();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Error deleting text mapping: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowValidationError(ValidationErrorType errorType, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
// if (ValidationHelper.ValidationMessages.TryGetValue(errorType, out (string Title, string Message) error))
|
||||
// {
|
||||
// ValidationTip.Title = error.Title;
|
||||
// ValidationTip.Subtitle = error.Message;
|
||||
// ValidationTip.IsOpen = true;
|
||||
// args.Cancel = true;
|
||||
// }
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_mappingService?.Dispose();
|
||||
_mappingService = null;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void ToggleSwitch_Toggled(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is ToggleSwitch toggleSwitch && toggleSwitch.DataContext is ProgramShortcut shortcut && _mappingService != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (toggleSwitch.IsOn)
|
||||
{
|
||||
bool saved = false;
|
||||
ShortcutKeyMapping shortcutKeyMapping = SettingsManager.EditorSettings.ShortcutSettingsDictionary[shortcut.Id].Shortcut;
|
||||
|
||||
saved = _mappingService.AddShorcutMapping(shortcutKeyMapping);
|
||||
|
||||
if (saved)
|
||||
{
|
||||
shortcut.IsActive = true;
|
||||
_mappingService.SaveSettings();
|
||||
SettingsManager.ToggleShortcutKeyMappingActiveState(shortcut.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
toggleSwitch.IsOn = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool deleted = false;
|
||||
if (shortcut.Shortcut.Count == 1)
|
||||
{
|
||||
// Single key mapping
|
||||
int originalKey = _mappingService.GetKeyCodeFromName(shortcut.Shortcut[0]);
|
||||
if (originalKey != 0)
|
||||
{
|
||||
deleted = _mappingService.DeleteSingleKeyToTextMapping(originalKey);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Shortcut mapping
|
||||
string originalKeys = string.Join(";", shortcut.Shortcut.Select(k => _mappingService.GetKeyCodeFromName(k)));
|
||||
deleted = _mappingService.DeleteShortcutMapping(originalKeys);
|
||||
}
|
||||
|
||||
if (deleted)
|
||||
{
|
||||
shortcut.IsActive = false;
|
||||
SettingsManager.ToggleShortcutKeyMappingActiveState(shortcut.Id);
|
||||
_mappingService.SaveSettings();
|
||||
}
|
||||
else
|
||||
{
|
||||
toggleSwitch.IsOn = true;
|
||||
}
|
||||
|
||||
LoadProgramShortcuts();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Error toggling shortcut active state: " + ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Page
|
||||
x:Class="KeyboardManagerEditorUI.Pages.Remappings"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:KeyboardManagerEditorUI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helper="using:KeyboardManagerEditorUI.Helpers"
|
||||
xmlns:local="using:KeyboardManagerEditorUI.Pages"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Padding="16">
|
||||
<StackPanel Orientation="Vertical" Spacing="12">
|
||||
<TextBlock
|
||||
x:Name="RemapInstruction"
|
||||
x:Uid="RemappingsPageInstructionTextBlock"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="Wrap" />
|
||||
<Button
|
||||
x:Name="NewRemappingBtn"
|
||||
Height="36"
|
||||
Margin="0,12,0,0"
|
||||
Click="NewRemappingBtn_Click">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<FontIcon
|
||||
FontSize="14"
|
||||
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
|
||||
Glyph="" />
|
||||
<TextBlock x:Uid="RemappingsPageNewTextBlock" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Grid
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="{ThemeResource LayerFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="{StaticResource OverlayCornerRadius}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="48" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="236" />
|
||||
<ColumnDefinition Width="236" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="84" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
x:Uid="RemappingsPageOriginalKeysTextBlock"
|
||||
Grid.Column="0"
|
||||
Margin="16,-2,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<AppBarSeparator
|
||||
Grid.Column="1"
|
||||
Margin="-6,4,0,4"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
x:Uid="RemappingsPageNewKeysTextBlock"
|
||||
Grid.Column="1"
|
||||
Margin="12,-2,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<AppBarSeparator
|
||||
Grid.Column="2"
|
||||
Margin="-6,4,0,4"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
x:Uid="RemmapingsPageApplicableAppsTextBlock"
|
||||
Grid.Column="2"
|
||||
Margin="12,-2,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
|
||||
<Rectangle
|
||||
Grid.ColumnSpan="4"
|
||||
Height="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Bottom"
|
||||
Fill="{ThemeResource CardStrokeColorDefaultBrush}" />
|
||||
<ListView
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="4"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="ListView_ItemClick"
|
||||
ItemsSource="{x:Bind RemappingList}"
|
||||
SelectionMode="None">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="helper:Remapping">
|
||||
<Grid Height="Auto" MinHeight="48">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" MinWidth="236" />
|
||||
<ColumnDefinition Width="Auto" MinWidth="236" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="84" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle
|
||||
Grid.ColumnSpan="5"
|
||||
Height="1"
|
||||
Margin="-16,0,-16,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Bottom"
|
||||
Fill="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
Opacity="0.8" />
|
||||
|
||||
<ItemsControl
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
IsEnabled="{x:Bind IsEnabled, Mode=OneWay}"
|
||||
ItemsSource="{x:Bind Shortcut}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<tkcontrols:WrapPanel
|
||||
MaxWidth="230"
|
||||
HorizontalSpacing="4"
|
||||
Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<controls:KeyVisual
|
||||
HorizontalAlignment="Left"
|
||||
Content="{Binding}"
|
||||
Style="{StaticResource DefaultKeyVisualStyle}" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<ItemsControl
|
||||
Grid.Column="1"
|
||||
Margin="0,6,0,6"
|
||||
VerticalAlignment="Center"
|
||||
IsEnabled="{x:Bind IsEnabled, Mode=OneWay}"
|
||||
ItemsSource="{x:Bind RemappedKeys}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<tkcontrols:WrapPanel
|
||||
MaxWidth="230"
|
||||
HorizontalSpacing="4"
|
||||
Orientation="Horizontal" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<controls:KeyVisual
|
||||
HorizontalAlignment="Left"
|
||||
Content="{Binding}"
|
||||
Style="{StaticResource AccentKeyVisualStyle}" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
Margin="-4,0,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="12"
|
||||
Text="{x:Bind AppName}" />
|
||||
<Button
|
||||
x:Uid="RemappingsPageDeleteButton"
|
||||
Grid.Column="3"
|
||||
Margin="0,0,4,0"
|
||||
Padding="8,4"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.Name="Delete remapping"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
Click="DeleteButton_Click">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||
FontSize="16"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Glyph="" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<ContentDialog
|
||||
x:Name="KeyDialog"
|
||||
x:Uid="RemappingsPageKeyDialog"
|
||||
MinWidth="600"
|
||||
MaxWidth="600"
|
||||
PrimaryButtonStyle="{StaticResource AccentButtonStyle}">
|
||||
<Grid>
|
||||
<controls:InputControl x:Name="RemappingControl" />
|
||||
</Grid>
|
||||
</ContentDialog>
|
||||
<TeachingTip
|
||||
x:Name="OrphanedKeysTeachingTip"
|
||||
x:Uid="RemmapingsPageOrphanedKeysTeachingTip"
|
||||
ActionButtonClick="OrphanedKeysTeachingTip_ActionButtonClick"
|
||||
ActionButtonStyle="{StaticResource AccentButtonStyle}"
|
||||
CloseButtonClick="OrphanedKeysTeachingTip_CloseButtonClick"
|
||||
IsLightDismissEnabled="False"
|
||||
PreferredPlacement="Center">
|
||||
<TeachingTip.IconSource>
|
||||
<SymbolIconSource Symbol="Important" />
|
||||
</TeachingTip.IconSource>
|
||||
</TeachingTip>
|
||||
<TeachingTip
|
||||
x:Name="ValidationTeachingTip"
|
||||
x:Uid="RemappingsPageValidationTeachingTip"
|
||||
CloseButtonClick="ValidationTeachingTip_CloseButtonClick"
|
||||
IsLightDismissEnabled="True"
|
||||
PreferredPlacement="Center">
|
||||
<TeachingTip.IconSource>
|
||||
<SymbolIconSource Symbol="Important" />
|
||||
</TeachingTip.IconSource>
|
||||
</TeachingTip>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -1,370 +0,0 @@
|
||||
// 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.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using KeyboardManagerEditorUI.Helpers;
|
||||
using KeyboardManagerEditorUI.Interop;
|
||||
using ManagedCommon;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using Microsoft.Windows.ApplicationModel.Resources;
|
||||
using Windows.ApplicationModel;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using static KeyboardManagerEditorUI.Helpers.ValidationHelper;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// The Remapping page that allow users to configure a single key or shortcut to a new key or shortcut
|
||||
/// </summary>
|
||||
public sealed partial class Remappings : Page, IDisposable
|
||||
{
|
||||
private KeyboardMappingService? _mappingService;
|
||||
|
||||
// Flag to indicate if the user is editing an existing remapping
|
||||
private bool _isEditMode;
|
||||
private Remapping? _editingRemapping;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
// The list of single key mappings
|
||||
public ObservableCollection<KeyMapping> SingleKeyMappings { get; } = new ObservableCollection<KeyMapping>();
|
||||
|
||||
// The list of shortcut key mappings
|
||||
public ObservableCollection<ShortcutKeyMapping> ShortcutKeyMappings { get; } = new ObservableCollection<ShortcutKeyMapping>();
|
||||
|
||||
// The full list of remappings
|
||||
public ObservableCollection<Remapping> RemappingList { get; set; }
|
||||
|
||||
public Remappings()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
RemappingList = new ObservableCollection<Remapping>();
|
||||
_mappingService = new KeyboardMappingService();
|
||||
|
||||
// Load all existing remappings
|
||||
LoadMappings();
|
||||
|
||||
this.Unloaded += Remappings_Unloaded;
|
||||
}
|
||||
|
||||
private void Remappings_Unloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Make sure we unregister the handler when the page is unloaded
|
||||
UnregisterWindowActivationHandler();
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
}
|
||||
|
||||
private void RegisterWindowActivationHandler()
|
||||
{
|
||||
// Get the current window that contains this page
|
||||
if (App.MainWindow is Window window)
|
||||
{
|
||||
// Register for window activation events
|
||||
window.Activated += Dialog_WindowActivated;
|
||||
}
|
||||
}
|
||||
|
||||
private void UnregisterWindowActivationHandler()
|
||||
{
|
||||
// Unregister to prevent memory leaks
|
||||
if (App.MainWindow is Window window)
|
||||
{
|
||||
window.Activated -= Dialog_WindowActivated;
|
||||
}
|
||||
}
|
||||
|
||||
private void Dialog_WindowActivated(object sender, WindowActivatedEventArgs args)
|
||||
{
|
||||
// When window is deactivated (user switched to another app)
|
||||
if (args.WindowActivationState == WindowActivationState.Deactivated)
|
||||
{
|
||||
// Make sure to cleanup the keyboard hook when the window loses focus
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
|
||||
RemappingControl.ResetToggleButtons();
|
||||
RemappingControl.UpdateAllAppsCheckBoxState();
|
||||
}
|
||||
}
|
||||
|
||||
private async void NewRemappingBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_isEditMode = false;
|
||||
_editingRemapping = null;
|
||||
|
||||
RemappingControl.SetOriginalKeys(new List<string>());
|
||||
RemappingControl.SetRemappedKeys(new List<string>());
|
||||
RemappingControl.SetApp(false, string.Empty);
|
||||
RemappingControl.SetUpToggleButtonInitialStatus();
|
||||
|
||||
RegisterWindowActivationHandler();
|
||||
|
||||
// Show the dialog to add a new remapping
|
||||
KeyDialog.PrimaryButtonClick += KeyDialog_PrimaryButtonClick;
|
||||
await KeyDialog.ShowAsync();
|
||||
KeyDialog.PrimaryButtonClick -= KeyDialog_PrimaryButtonClick;
|
||||
|
||||
UnregisterWindowActivationHandler();
|
||||
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
}
|
||||
|
||||
private async void ListView_ItemClick(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
if (e.ClickedItem is Remapping selectedRemapping && selectedRemapping.IsEnabled)
|
||||
{
|
||||
// Set to edit mode
|
||||
_isEditMode = true;
|
||||
_editingRemapping = selectedRemapping;
|
||||
|
||||
RemappingControl.SetOriginalKeys(selectedRemapping.Shortcut);
|
||||
RemappingControl.SetRemappedKeys(selectedRemapping.RemappedKeys);
|
||||
RemappingControl.SetApp(!selectedRemapping.IsAllApps, selectedRemapping.AppName);
|
||||
RemappingControl.SetUpToggleButtonInitialStatus();
|
||||
|
||||
RegisterWindowActivationHandler();
|
||||
|
||||
KeyDialog.PrimaryButtonClick += KeyDialog_PrimaryButtonClick;
|
||||
await KeyDialog.ShowAsync();
|
||||
KeyDialog.PrimaryButtonClick -= KeyDialog_PrimaryButtonClick;
|
||||
|
||||
UnregisterWindowActivationHandler();
|
||||
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
|
||||
// Reset the edit status
|
||||
_isEditMode = false;
|
||||
_editingRemapping = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void KeyDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
List<string> originalKeys = RemappingControl.GetOriginalKeys();
|
||||
List<string> remappedKeys = RemappingControl.GetRemappedKeys();
|
||||
bool isAppSpecific = RemappingControl.GetIsAppSpecific();
|
||||
string appName = RemappingControl.GetAppName();
|
||||
|
||||
// Make sure _mappingService is not null before validating and saving
|
||||
if (_mappingService == null)
|
||||
{
|
||||
Logger.LogError("Mapping service is null, cannot validate mapping");
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate the remapping
|
||||
ValidationErrorType errorType = ValidationHelper.ValidateKeyMapping(
|
||||
originalKeys, remappedKeys, isAppSpecific, appName, _mappingService, _isEditMode, _editingRemapping);
|
||||
|
||||
if (errorType != ValidationErrorType.NoError)
|
||||
{
|
||||
ShowValidationError(errorType, args);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for orphaned keys
|
||||
if (originalKeys.Count == 1 && _mappingService != null)
|
||||
{
|
||||
int originalKeyCode = _mappingService.GetKeyCodeFromName(originalKeys[0]);
|
||||
|
||||
if (IsKeyOrphaned(originalKeyCode, _mappingService))
|
||||
{
|
||||
string keyName = _mappingService.GetKeyDisplayName(originalKeyCode);
|
||||
|
||||
OrphanedKeysTeachingTip.Target = RemappingControl;
|
||||
OrphanedKeysTeachingTip.Subtitle = $"The key {keyName} will become orphaned (inaccessible) after remapping. Please confirm if you want to proceed.";
|
||||
OrphanedKeysTeachingTip.Tag = args;
|
||||
OrphanedKeysTeachingTip.IsOpen = true;
|
||||
|
||||
args.Cancel = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If in edit mode, delete the existing remapping before saving the new one
|
||||
if (_isEditMode && _editingRemapping != null)
|
||||
{
|
||||
if (!RemappingHelper.DeleteRemapping(_mappingService!, _editingRemapping))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If no errors, proceed to save the remapping
|
||||
bool saved = RemappingHelper.SaveMapping(_mappingService!, originalKeys, remappedKeys, isAppSpecific, appName);
|
||||
if (saved)
|
||||
{
|
||||
// Display the remapping in the list after saving
|
||||
LoadMappings();
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is Button button && button.DataContext is Remapping remapping)
|
||||
{
|
||||
if (RemappingHelper.DeleteRemapping(_mappingService!, remapping))
|
||||
{
|
||||
LoadMappings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidationTeachingTip_CloseButtonClick(TeachingTip sender, object args)
|
||||
{
|
||||
sender.IsOpen = false;
|
||||
}
|
||||
|
||||
private void OrphanedKeysTeachingTip_ActionButtonClick(TeachingTip sender, object args)
|
||||
{
|
||||
// User pressed continue anyway button
|
||||
sender.IsOpen = false;
|
||||
|
||||
if (_isEditMode && _editingRemapping != null)
|
||||
{
|
||||
if (!RemappingHelper.DeleteRemapping(_mappingService!, _editingRemapping))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool saved = RemappingHelper.SaveMapping(
|
||||
_mappingService!, RemappingControl.GetOriginalKeys(), RemappingControl.GetRemappedKeys(), RemappingControl.GetIsAppSpecific(), RemappingControl.GetAppName());
|
||||
if (saved)
|
||||
{
|
||||
KeyDialog.Hide();
|
||||
LoadMappings();
|
||||
}
|
||||
}
|
||||
|
||||
private void OrphanedKeysTeachingTip_CloseButtonClick(TeachingTip sender, object args)
|
||||
{
|
||||
// Just close the teaching tip if the user canceled
|
||||
sender.IsOpen = false;
|
||||
}
|
||||
|
||||
private void LoadMappings()
|
||||
{
|
||||
if (_mappingService == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SingleKeyMappings.Clear();
|
||||
ShortcutKeyMappings.Clear();
|
||||
RemappingList.Clear();
|
||||
|
||||
// Load all single key mappings
|
||||
foreach (var mapping in _mappingService.GetSingleKeyMappings())
|
||||
{
|
||||
SingleKeyMappings.Add(mapping);
|
||||
|
||||
string[] targetKeyCodes = mapping.TargetKey.Split(';');
|
||||
var targetKeyNames = new List<string>();
|
||||
|
||||
foreach (var keyCode in targetKeyCodes)
|
||||
{
|
||||
if (int.TryParse(keyCode, out int code))
|
||||
{
|
||||
targetKeyNames.Add(_mappingService.GetKeyDisplayName(code));
|
||||
}
|
||||
}
|
||||
|
||||
RemappingList.Add(new Remapping
|
||||
{
|
||||
Shortcut = new List<string> { _mappingService.GetKeyDisplayName(mapping.OriginalKey) },
|
||||
RemappedKeys = targetKeyNames,
|
||||
IsAllApps = true,
|
||||
});
|
||||
}
|
||||
|
||||
// Load all shortcut key mappings
|
||||
foreach (var mapping in _mappingService.GetShortcutMappingsByType(ShortcutOperationType.RemapShortcut))
|
||||
{
|
||||
ShortcutKeyMappings.Add(mapping);
|
||||
|
||||
string[] originalKeyCodes = mapping.OriginalKeys.Split(';');
|
||||
string[] targetKeyCodes = mapping.TargetKeys.Split(';');
|
||||
|
||||
var originalKeyNames = new List<string>();
|
||||
var targetKeyNames = new List<string>();
|
||||
|
||||
foreach (var keyCode in originalKeyCodes)
|
||||
{
|
||||
if (int.TryParse(keyCode, out int code))
|
||||
{
|
||||
originalKeyNames.Add(_mappingService.GetKeyDisplayName(code));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var keyCode in targetKeyCodes)
|
||||
{
|
||||
if (int.TryParse(keyCode, out int code))
|
||||
{
|
||||
targetKeyNames.Add(_mappingService.GetKeyDisplayName(code));
|
||||
}
|
||||
}
|
||||
|
||||
RemappingList.Add(new Remapping
|
||||
{
|
||||
Shortcut = originalKeyNames,
|
||||
RemappedKeys = targetKeyNames,
|
||||
IsAllApps = string.IsNullOrEmpty(mapping.TargetApp),
|
||||
AppName = string.IsNullOrEmpty(mapping.TargetApp) ? string.Empty : mapping.TargetApp,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private bool ShowValidationError(ValidationErrorType errorType, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
var (title, message) = ValidationMessages[errorType];
|
||||
|
||||
ValidationTeachingTip.Title = title;
|
||||
ValidationTeachingTip.Subtitle = message;
|
||||
ValidationTeachingTip.Target = RemappingControl;
|
||||
ValidationTeachingTip.Tag = args;
|
||||
ValidationTeachingTip.IsOpen = true;
|
||||
args.Cancel = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// Dispose managed resources
|
||||
_mappingService?.Dispose();
|
||||
_mappingService = null;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,200 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Page
|
||||
x:Class="KeyboardManagerEditorUI.Pages.Text"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:KeyboardManagerEditorUI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helper="using:KeyboardManagerEditorUI.Helpers"
|
||||
xmlns:local="using:KeyboardManagerEditorUI.Pages"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:tkcontrols="using:CommunityToolkit.WinUI.UI.Controls"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Padding="16">
|
||||
<StackPanel Orientation="Vertical" Spacing="12">
|
||||
<TextBlock
|
||||
x:Uid="TextPageInstructionTextBlock"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="Wrap" />
|
||||
<Button
|
||||
x:Name="NewShortcutBtn"
|
||||
Height="36"
|
||||
Margin="0,12,0,0"
|
||||
Click="NewShortcutBtn_Click">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<FontIcon
|
||||
FontSize="14"
|
||||
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
|
||||
Glyph="" />
|
||||
<TextBlock x:Uid="TextPageNewTextBlock" VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Grid
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="{ThemeResource LayerFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="{StaticResource OverlayCornerRadius}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="48" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="236" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="120" />
|
||||
<ColumnDefinition Width="84" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
x:Uid="TextPageOriginalKeysTextBlock"
|
||||
Grid.Column="0"
|
||||
Margin="16,-2,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<AppBarSeparator
|
||||
Grid.Column="1"
|
||||
Margin="-6,4,0,4"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
x:Uid="TextPageTextTextBlock"
|
||||
Grid.Column="1"
|
||||
Margin="12,-2,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<AppBarSeparator
|
||||
Grid.Column="2"
|
||||
Margin="-6,4,0,4"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
x:Uid="TextPageApplicableAppsTextBlock"
|
||||
Grid.Column="2"
|
||||
Margin="12,-2,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<Rectangle
|
||||
Grid.Row="0"
|
||||
Grid.ColumnSpan="4"
|
||||
Height="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Bottom"
|
||||
Fill="{ThemeResource CardStrokeColorDefaultBrush}" />
|
||||
|
||||
<ListView
|
||||
x:Name="MappingsList"
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="4"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="ListView_ItemClick"
|
||||
ItemsSource="{x:Bind TextMappings}"
|
||||
SelectionMode="None">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="helper:TextMapping">
|
||||
<Grid MinHeight="48">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="236" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="120" />
|
||||
<ColumnDefinition Width="48" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle
|
||||
Grid.ColumnSpan="4"
|
||||
Height="1"
|
||||
Margin="-16,0,-16,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Bottom"
|
||||
Fill="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
Opacity="0.8" />
|
||||
|
||||
<!-- Shortcut keys -->
|
||||
<ItemsControl
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
ItemsSource="{x:Bind Shortcut}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal" Spacing="4" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<controls:KeyVisual
|
||||
HorizontalAlignment="Left"
|
||||
Content="{Binding}"
|
||||
Style="{StaticResource DefaultKeyVisualStyle}" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
<!-- Text content -->
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="-4,0,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="12"
|
||||
Text="{x:Bind Text}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
|
||||
<!-- App name -->
|
||||
<TextBlock
|
||||
Grid.Column="2"
|
||||
Margin="-4,0,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="12"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="{x:Bind AppName}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
|
||||
<!-- Delete button -->
|
||||
<Button
|
||||
x:Uid="TextPageDeleteButton"
|
||||
Grid.Column="3"
|
||||
Margin="0,0,4,0"
|
||||
Padding="8,4"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.Name="Delete remapping"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
Click="DeleteButton_Click">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||
FontSize="16"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Glyph="" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
|
||||
<ContentDialog
|
||||
x:Name="KeyDialog"
|
||||
x:Uid="TextPageKeyDialog"
|
||||
Width="480"
|
||||
Height="360"
|
||||
MinWidth="600"
|
||||
MaxWidth="600"
|
||||
PrimaryButtonStyle="{StaticResource AccentButtonStyle}">
|
||||
<Grid>
|
||||
<controls:TextPageInputControl x:Name="TextInputControl" />
|
||||
</Grid>
|
||||
</ContentDialog>
|
||||
|
||||
<TeachingTip
|
||||
x:Name="ValidationTip"
|
||||
x:Uid="TextPageValidationTip"
|
||||
IsLightDismissEnabled="True"
|
||||
PreferredPlacement="Center">
|
||||
<TeachingTip.IconSource>
|
||||
<SymbolIconSource Symbol="Important" />
|
||||
</TeachingTip.IconSource>
|
||||
</TeachingTip>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -1,332 +0,0 @@
|
||||
// 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.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using KeyboardManagerEditorUI.Helpers;
|
||||
using KeyboardManagerEditorUI.Interop;
|
||||
using ManagedCommon;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Pages
|
||||
{
|
||||
public sealed partial class Text : Page, IDisposable
|
||||
{
|
||||
private KeyboardMappingService? _mappingService;
|
||||
|
||||
// Flag to indicate if the user is editing an existing mapping
|
||||
private bool _isEditMode;
|
||||
private TextMapping? _editingMapping;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
// The list of text mappings
|
||||
public ObservableCollection<TextMapping> TextMappings { get; } = new ObservableCollection<TextMapping>();
|
||||
|
||||
public Text()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
try
|
||||
{
|
||||
_mappingService = new KeyboardMappingService();
|
||||
LoadTextMappings();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Failed to initialize KeyboardMappingService: " + ex.Message);
|
||||
System.Diagnostics.Debug.WriteLine(ex.Message);
|
||||
}
|
||||
|
||||
this.Unloaded += Text_Unloaded;
|
||||
}
|
||||
|
||||
private void Text_Unloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// Make sure we unregister the handler when the page is unloaded
|
||||
UnregisterWindowActivationHandler();
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
}
|
||||
|
||||
private void RegisterWindowActivationHandler()
|
||||
{
|
||||
// Get the current window that contains this page
|
||||
if (App.MainWindow is Window window)
|
||||
{
|
||||
// Register for window activation events
|
||||
window.Activated += Dialog_WindowActivated;
|
||||
}
|
||||
}
|
||||
|
||||
private void UnregisterWindowActivationHandler()
|
||||
{
|
||||
// Unregister to prevent memory leaks
|
||||
if (App.MainWindow is Window window)
|
||||
{
|
||||
window.Activated -= Dialog_WindowActivated;
|
||||
}
|
||||
}
|
||||
|
||||
private void Dialog_WindowActivated(object sender, WindowActivatedEventArgs args)
|
||||
{
|
||||
// When window is deactivated (user switched to another app)
|
||||
if (args.WindowActivationState == WindowActivationState.Deactivated)
|
||||
{
|
||||
// Make sure to cleanup the keyboard hook when the window loses focus
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
|
||||
TextInputControl.ClearKeys();
|
||||
TextInputControl.UpdateAllAppsCheckBoxState();
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadTextMappings()
|
||||
{
|
||||
if (_mappingService == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TextMappings.Clear();
|
||||
|
||||
// Load key-to-text mappings
|
||||
var keyToTextMappings = _mappingService.GetKeyToTextMappings();
|
||||
foreach (var mapping in keyToTextMappings)
|
||||
{
|
||||
TextMappings.Add(new TextMapping
|
||||
{
|
||||
Shortcut = new List<string> { _mappingService.GetKeyDisplayName(mapping.OriginalKey) },
|
||||
Text = mapping.TargetText,
|
||||
IsAllApps = true,
|
||||
AppName = string.Empty,
|
||||
});
|
||||
}
|
||||
|
||||
// Load shortcut-to-text mappings
|
||||
foreach (var mapping in _mappingService.GetShortcutMappingsByType(ShortcutOperationType.RemapText))
|
||||
{
|
||||
string[] originalKeyCodes = mapping.OriginalKeys.Split(';');
|
||||
var originalKeyNames = new List<string>();
|
||||
foreach (var keyCode in originalKeyCodes)
|
||||
{
|
||||
if (int.TryParse(keyCode, out int code))
|
||||
{
|
||||
originalKeyNames.Add(_mappingService.GetKeyDisplayName(code));
|
||||
}
|
||||
}
|
||||
|
||||
TextMappings.Add(new TextMapping
|
||||
{
|
||||
Shortcut = originalKeyNames,
|
||||
Text = mapping.TargetText,
|
||||
IsAllApps = string.IsNullOrEmpty(mapping.TargetApp),
|
||||
AppName = string.IsNullOrEmpty(mapping.TargetApp) ? string.Empty : mapping.TargetApp,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async void NewShortcutBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_isEditMode = false;
|
||||
_editingMapping = null;
|
||||
|
||||
TextInputControl.ClearKeys();
|
||||
TextInputControl.SetTextContent(string.Empty);
|
||||
TextInputControl.SetAppSpecific(false, string.Empty);
|
||||
|
||||
RegisterWindowActivationHandler();
|
||||
|
||||
KeyDialog.PrimaryButtonClick += KeyDialog_PrimaryButtonClick;
|
||||
await KeyDialog.ShowAsync();
|
||||
KeyDialog.PrimaryButtonClick -= KeyDialog_PrimaryButtonClick;
|
||||
|
||||
UnregisterWindowActivationHandler();
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
}
|
||||
|
||||
private async void ListView_ItemClick(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
if (e.ClickedItem is TextMapping selectedMapping)
|
||||
{
|
||||
_isEditMode = true;
|
||||
_editingMapping = selectedMapping;
|
||||
|
||||
TextInputControl.SetShortcutKeys(selectedMapping.Shortcut);
|
||||
TextInputControl.SetTextContent(selectedMapping.Text);
|
||||
TextInputControl.SetAppSpecific(!selectedMapping.IsAllApps, selectedMapping.AppName);
|
||||
|
||||
RegisterWindowActivationHandler();
|
||||
|
||||
KeyDialog.PrimaryButtonClick += KeyDialog_PrimaryButtonClick;
|
||||
await KeyDialog.ShowAsync();
|
||||
KeyDialog.PrimaryButtonClick -= KeyDialog_PrimaryButtonClick;
|
||||
|
||||
UnregisterWindowActivationHandler();
|
||||
|
||||
KeyboardHookHelper.Instance.CleanupHook();
|
||||
|
||||
// Reset the edit status
|
||||
_isEditMode = false;
|
||||
_editingMapping = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void KeyDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
if (_mappingService == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<string> keys = TextInputControl.GetShortcutKeys();
|
||||
string textContent = TextInputControl.GetTextContent();
|
||||
bool isAppSpecific = TextInputControl.GetIsAppSpecific();
|
||||
string appName = TextInputControl.GetAppName();
|
||||
|
||||
// Validate inputs
|
||||
ValidationErrorType errorType = ValidationHelper.ValidateTextMapping(
|
||||
keys, textContent, isAppSpecific, appName, _mappingService);
|
||||
|
||||
if (errorType != ValidationErrorType.NoError)
|
||||
{
|
||||
ShowValidationError(errorType, args);
|
||||
return;
|
||||
}
|
||||
|
||||
bool saved = false;
|
||||
|
||||
try
|
||||
{
|
||||
// Delete existing mapping if in edit mode
|
||||
if (_isEditMode && _editingMapping != null)
|
||||
{
|
||||
if (_editingMapping.Shortcut.Count == 1)
|
||||
{
|
||||
int originalKey = _mappingService.GetKeyCodeFromName(_editingMapping.Shortcut[0]);
|
||||
if (originalKey != 0)
|
||||
{
|
||||
_mappingService.DeleteSingleKeyToTextMapping(originalKey);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string originalKeys = string.Join(";", _editingMapping.Shortcut.Select(k => _mappingService.GetKeyCodeFromName(k).ToString(CultureInfo.InvariantCulture)));
|
||||
_mappingService.DeleteShortcutMapping(originalKeys, _editingMapping.IsAllApps ? string.Empty : _editingMapping.AppName);
|
||||
}
|
||||
}
|
||||
|
||||
// Add new mapping
|
||||
if (keys.Count == 1)
|
||||
{
|
||||
// Single key to text mapping
|
||||
int originalKey = _mappingService.GetKeyCodeFromName(keys[0]);
|
||||
if (originalKey != 0)
|
||||
{
|
||||
saved = _mappingService.AddSingleKeyToTextMapping(originalKey, textContent);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Shortcut to text mapping
|
||||
string originalKeysString = string.Join(";", keys.Select(k => _mappingService.GetKeyCodeFromName(k).ToString(CultureInfo.InvariantCulture)));
|
||||
|
||||
if (isAppSpecific && !string.IsNullOrEmpty(appName))
|
||||
{
|
||||
saved = _mappingService.AddShortcutMapping(originalKeysString, textContent, appName, ShortcutOperationType.RemapText);
|
||||
}
|
||||
else
|
||||
{
|
||||
saved = _mappingService.AddShortcutMapping(originalKeysString, textContent, operationType: ShortcutOperationType.RemapText);
|
||||
}
|
||||
}
|
||||
|
||||
if (saved)
|
||||
{
|
||||
_mappingService.SaveSettings();
|
||||
LoadTextMappings(); // Refresh the list
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Error saving text mapping: " + ex.Message);
|
||||
args.Cancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_mappingService == null || !(sender is Button button) || !(button.DataContext is TextMapping mapping))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
bool deleted = false;
|
||||
if (mapping.Shortcut.Count == 1)
|
||||
{
|
||||
// Single key mapping
|
||||
int originalKey = _mappingService.GetKeyCodeFromName(mapping.Shortcut[0]);
|
||||
if (originalKey != 0)
|
||||
{
|
||||
deleted = _mappingService.DeleteSingleKeyToTextMapping(originalKey);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Shortcut mapping
|
||||
string originalKeys = string.Join(";", mapping.Shortcut.Select(k => _mappingService.GetKeyCodeFromName(k)));
|
||||
deleted = _mappingService.DeleteShortcutMapping(originalKeys, mapping.IsAllApps ? string.Empty : mapping.AppName);
|
||||
}
|
||||
|
||||
if (deleted)
|
||||
{
|
||||
_mappingService.SaveSettings();
|
||||
TextMappings.Remove(mapping);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Error deleting text mapping: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowValidationError(ValidationErrorType errorType, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
if (ValidationHelper.ValidationMessages.TryGetValue(errorType, out (string Title, string Message) error))
|
||||
{
|
||||
ValidationTip.Title = error.Title;
|
||||
ValidationTip.Subtitle = error.Message;
|
||||
ValidationTip.IsOpen = true;
|
||||
args.Cancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_mappingService?.Dispose();
|
||||
_mappingService = null;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Page
|
||||
x:Class="KeyboardManagerEditorUI.Pages.URLs"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:KeyboardManagerEditorUI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:helper="using:KeyboardManagerEditorUI.Helpers"
|
||||
xmlns:local="using:KeyboardManagerEditorUI.Pages"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid Padding="16">
|
||||
<StackPanel
|
||||
Orientation="Vertical"
|
||||
Spacing="12">
|
||||
<TextBlock
|
||||
x:Uid="UrlPageInstructionTextBlock"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="Wrap" />
|
||||
<Button
|
||||
x:Name="NewShortcutBtn"
|
||||
Height="36"
|
||||
Margin="0,12,0,0"
|
||||
Click="NewShortcutBtn_Click">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8">
|
||||
<FontIcon
|
||||
FontSize="14"
|
||||
Foreground="{ThemeResource AccentTextFillColorPrimaryBrush}"
|
||||
Glyph="" />
|
||||
<TextBlock
|
||||
x:Uid="UrlPageNewTextBlock"
|
||||
VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Grid
|
||||
HorizontalAlignment="Stretch"
|
||||
Background="{ThemeResource LayerFillColorDefaultBrush}"
|
||||
BorderBrush="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="{ThemeResource OverlayCornerRadius}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="48" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="348" />
|
||||
<ColumnDefinition Width="236" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="84" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
x:Uid="UrlPageShortcutTextBlock"
|
||||
Grid.Column="0"
|
||||
Margin="16,-2,0,0"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<AppBarSeparator
|
||||
Grid.Column="1"
|
||||
Margin="-6,4,0,4"
|
||||
HorizontalAlignment="Left" />
|
||||
<TextBlock
|
||||
x:Uid="UrlPageUrlTextBlock"
|
||||
Grid.Column="1"
|
||||
Margin="12,-2,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}" />
|
||||
<Rectangle
|
||||
Grid.ColumnSpan="4"
|
||||
Height="1"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Bottom"
|
||||
Fill="{ThemeResource CardStrokeColorDefaultBrush}" />
|
||||
<ListView
|
||||
Grid.Row="1"
|
||||
Grid.ColumnSpan="4"
|
||||
IsItemClickEnabled="True"
|
||||
ItemClick="ListView_ItemClick"
|
||||
ItemsSource="{x:Bind Shortcuts}"
|
||||
SelectionMode="None">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="helper:URLShortcut">
|
||||
<Grid Height="48">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="348" />
|
||||
<ColumnDefinition Width="476" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="84" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle
|
||||
Grid.ColumnSpan="5"
|
||||
Height="1"
|
||||
Margin="-16,0,-16,0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Bottom"
|
||||
Fill="{ThemeResource CardStrokeColorDefaultBrush}"
|
||||
Opacity="0.8" />
|
||||
<ItemsControl
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center"
|
||||
ItemsSource="{x:Bind Shortcut}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal" Spacing="4" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<controls:KeyVisual
|
||||
HorizontalAlignment="Left"
|
||||
Content="{Binding}"
|
||||
Style="{StaticResource DefaultKeyVisualStyle}" />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
<HyperlinkButton
|
||||
Grid.Column="1"
|
||||
Margin="-12,0,0,0"
|
||||
Content="{x:Bind URL}" />
|
||||
<Button
|
||||
x:Uid="UrlPageDeleteButton"
|
||||
Grid.ColumnSpan="4"
|
||||
Margin="0,0,4,0"
|
||||
Padding="8,4"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
AutomationProperties.Name="Delete URL shortcut"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
Click="DeleteButton_Click">
|
||||
<FontIcon
|
||||
FontFamily="{StaticResource SymbolThemeFontFamily}"
|
||||
FontSize="16"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Glyph="" />
|
||||
</Button>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<ContentDialog
|
||||
x:Name="KeyDialog"
|
||||
x:Uid="UrlPageKeyDialog"
|
||||
Width="480"
|
||||
Height="360"
|
||||
MinWidth="600"
|
||||
MaxWidth="600"
|
||||
PrimaryButtonClick="KeyDialog_PrimaryButtonClick"
|
||||
PrimaryButtonStyle="{StaticResource AccentButtonStyle}">
|
||||
<Grid>
|
||||
<controls:UrlPageInputControl x:Name="UrlShortcutControl" />
|
||||
</Grid>
|
||||
</ContentDialog>
|
||||
</Grid>
|
||||
</Page>
|
||||
@@ -1,261 +0,0 @@
|
||||
// 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.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Text;
|
||||
using KeyboardManagerEditorUI.Helpers;
|
||||
using KeyboardManagerEditorUI.Interop;
|
||||
using ManagedCommon;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using Microsoft.UI.Xaml.Navigation;
|
||||
using Windows.Foundation;
|
||||
using Windows.Foundation.Collections;
|
||||
using static KeyboardManagerEditorUI.Interop.ShortcutKeyMapping;
|
||||
|
||||
namespace KeyboardManagerEditorUI.Pages
|
||||
{
|
||||
/// <summary>
|
||||
/// An empty page that can be used on its own or navigated to within a Frame.
|
||||
/// </summary>
|
||||
public sealed partial class URLs : Page, IDisposable
|
||||
{
|
||||
private KeyboardMappingService? _mappingService;
|
||||
|
||||
private bool _disposed;
|
||||
|
||||
private bool _isEditMode;
|
||||
private URLShortcut? _editingMapping;
|
||||
|
||||
public ObservableCollection<URLShortcut> Shortcuts { get; set; }
|
||||
|
||||
[DllImport("PowerToys.KeyboardManagerEditorLibraryWrapper.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
|
||||
private static extern void GetKeyDisplayName(int keyCode, [Out] StringBuilder keyName, int maxLength);
|
||||
|
||||
public URLs()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
|
||||
Shortcuts = new ObservableCollection<URLShortcut>();
|
||||
|
||||
_mappingService = new KeyboardMappingService();
|
||||
|
||||
LoadUrlShortcuts();
|
||||
}
|
||||
|
||||
public void LoadUrlShortcuts()
|
||||
{
|
||||
if (_mappingService == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var mapping in _mappingService.GetShortcutMappingsByType(ShortcutOperationType.OpenUri))
|
||||
{
|
||||
string[] originalKeyCodes = mapping.OriginalKeys.Split(';');
|
||||
var originalKeyNames = new List<string>();
|
||||
foreach (var keyCode in originalKeyCodes)
|
||||
{
|
||||
if (int.TryParse(keyCode, out int code))
|
||||
{
|
||||
originalKeyNames.Add(GetKeyDisplayName(code));
|
||||
}
|
||||
}
|
||||
|
||||
var shortcut = new URLShortcut
|
||||
{
|
||||
Shortcut = originalKeyNames,
|
||||
URL = mapping.UriToOpen,
|
||||
};
|
||||
|
||||
Shortcuts.Add(shortcut);
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetKeyDisplayName(int keyCode)
|
||||
{
|
||||
var keyName = new StringBuilder(64);
|
||||
GetKeyDisplayName(keyCode, keyName, keyName.Capacity);
|
||||
return keyName.ToString();
|
||||
}
|
||||
|
||||
private async void NewShortcutBtn_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_isEditMode = false;
|
||||
_editingMapping = null;
|
||||
|
||||
UrlShortcutControl.ClearKeys();
|
||||
UrlShortcutControl.SetUrlPathContent(string.Empty);
|
||||
await KeyDialog.ShowAsync();
|
||||
}
|
||||
|
||||
private async void ListView_ItemClick(object sender, ItemClickEventArgs e)
|
||||
{
|
||||
if (e.ClickedItem is URLShortcut urlShortcut)
|
||||
{
|
||||
_isEditMode = true;
|
||||
_editingMapping = urlShortcut;
|
||||
|
||||
UrlShortcutControl.SetShortcutKeys(urlShortcut.Shortcut);
|
||||
UrlShortcutControl.SetUrlPathContent(urlShortcut.URL);
|
||||
await KeyDialog.ShowAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// Dispose managed resources
|
||||
_mappingService?.Dispose();
|
||||
_mappingService = null;
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void KeyDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
if (_mappingService == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<string> keys = UrlShortcutControl.GetShortcutKeys();
|
||||
string urlPath = UrlShortcutControl.GetUrlPathContent();
|
||||
|
||||
// Validate inputs
|
||||
ValidationErrorType errorType = ValidationHelper.ValidateProgramOrUrlMapping(keys, false, string.Empty, _mappingService);
|
||||
|
||||
if (errorType != ValidationErrorType.NoError)
|
||||
{
|
||||
ShowValidationError(errorType, args);
|
||||
return;
|
||||
}
|
||||
|
||||
bool saved = false;
|
||||
|
||||
try
|
||||
{
|
||||
// Delete existing mapping if in edit mode
|
||||
if (_isEditMode && _editingMapping != null)
|
||||
{
|
||||
if (_editingMapping.Shortcut.Count == 1)
|
||||
{
|
||||
int originalKey = _mappingService.GetKeyCodeFromName(_editingMapping.Shortcut[0]);
|
||||
if (originalKey != 0)
|
||||
{
|
||||
_mappingService.DeleteSingleKeyMapping(originalKey);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string originalKeys = string.Join(";", _editingMapping.Shortcut.Select(k => _mappingService.GetKeyCodeFromName(k).ToString(CultureInfo.InvariantCulture)));
|
||||
_mappingService.DeleteShortcutMapping(originalKeys);
|
||||
}
|
||||
}
|
||||
|
||||
// Shortcut to text mapping
|
||||
string originalKeysString = string.Join(";", keys.Select(k => _mappingService.GetKeyCodeFromName(k).ToString(CultureInfo.InvariantCulture)));
|
||||
|
||||
// if (isAppSpecific && !string.IsNullOrEmpty(appName))
|
||||
// {
|
||||
// saved = _mappingService.AddShortcutMapping(originalKeysString, programPath, appName, ShortcutOperationType.RemapText);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
ShortcutKeyMapping shortcutKeyMapping = new ShortcutKeyMapping()
|
||||
{
|
||||
OperationType = ShortcutOperationType.OpenUri,
|
||||
OriginalKeys = originalKeysString,
|
||||
TargetKeys = originalKeysString,
|
||||
UriToOpen = urlPath,
|
||||
};
|
||||
|
||||
saved = _mappingService.AddShorcutMapping(shortcutKeyMapping);
|
||||
|
||||
if (saved)
|
||||
{
|
||||
_mappingService.SaveSettings();
|
||||
LoadUrlShortcuts();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Error saving text mapping: " + ex.Message);
|
||||
args.Cancel = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowValidationError(ValidationErrorType errorType, ContentDialogButtonClickEventArgs args)
|
||||
{
|
||||
// if (ValidationHelper.ValidationMessages.TryGetValue(errorType, out (string Title, string Message) error))
|
||||
// {
|
||||
// ValidationTip.Title = error.Title;
|
||||
// ValidationTip.Subtitle = error.Message;
|
||||
// ValidationTip.IsOpen = true;
|
||||
// args.Cancel = true;
|
||||
// }
|
||||
}
|
||||
|
||||
private void DeleteButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_mappingService == null || !(sender is Button button) || !(button.DataContext is URLShortcut shortcut))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
bool deleted = false;
|
||||
if (shortcut.Shortcut.Count == 1)
|
||||
{
|
||||
// Single key mapping
|
||||
int originalKey = _mappingService.GetKeyCodeFromName(shortcut.Shortcut[0]);
|
||||
if (originalKey != 0)
|
||||
{
|
||||
deleted = _mappingService.DeleteSingleKeyToTextMapping(originalKey);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Shortcut mapping
|
||||
string originalKeys = string.Join(";", shortcut.Shortcut.Select(k => _mappingService.GetKeyCodeFromName(k)));
|
||||
deleted = _mappingService.DeleteShortcutMapping(originalKeys);
|
||||
}
|
||||
|
||||
if (deleted)
|
||||
{
|
||||
_mappingService.SaveSettings();
|
||||
Shortcuts.Remove(shortcut);
|
||||
LoadUrlShortcuts();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError("Error deleting text mapping: " + ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user