2020-03-27 23:38:58 +08:00
# include "pch.h"
# include "SingleKeyRemapControl.h"
//Both static members are initialized to null
HWND SingleKeyRemapControl : : EditKeyboardWindowHandle = nullptr ;
KeyboardManagerState * SingleKeyRemapControl : : keyboardManagerState = nullptr ;
2020-04-10 00:20:19 +08:00
// Initialized as new vector
std : : vector < std : : vector < DWORD > > SingleKeyRemapControl : : singleKeyRemapBuffer ;
2020-03-27 23:38:58 +08:00
// Function to add a new row to the remap keys table. If the originalKey and newKey args are provided, then the displayed remap keys are set to those values.
2020-04-19 07:12:26 +08:00
void SingleKeyRemapControl : : AddNewControlKeyRemapRow ( StackPanel & parent , std : : vector < std : : vector < std : : unique_ptr < SingleKeyRemapControl > > > & keyboardRemapControlObjects , const DWORD originalKey , const DWORD newKey )
2020-03-27 23:38:58 +08:00
{
// Parent element for the row
Windows : : UI : : Xaml : : Controls : : StackPanel tableRow ;
tableRow . Background ( Windows : : UI : : Xaml : : Media : : SolidColorBrush { Windows : : UI : : Colors : : LightGray ( ) } ) ;
tableRow . Spacing ( 100 ) ;
tableRow . Orientation ( Windows : : UI : : Xaml : : Controls : : Orientation : : Horizontal ) ;
2020-04-19 07:12:26 +08:00
// Create new SingleKeyRemapControl objects dynamically so that we does not get destructed
std : : vector < std : : unique_ptr < SingleKeyRemapControl > > newrow ;
newrow . push_back ( std : : move ( std : : unique_ptr < SingleKeyRemapControl > ( new SingleKeyRemapControl ( singleKeyRemapBuffer . size ( ) , 0 ) ) ) ) ;
newrow . push_back ( std : : move ( std : : unique_ptr < SingleKeyRemapControl > ( new SingleKeyRemapControl ( singleKeyRemapBuffer . size ( ) , 1 ) ) ) ) ;
keyboardRemapControlObjects . push_back ( std : : move ( newrow ) ) ;
2020-03-27 23:38:58 +08:00
// SingleKeyRemapControl for the original key.
2020-04-19 07:12:26 +08:00
tableRow . Children ( ) . Append ( keyboardRemapControlObjects [ keyboardRemapControlObjects . size ( ) - 1 ] [ 0 ] - > getSingleKeyRemapControl ( ) ) ;
// SingleKeyRemapControl for the new remap key
tableRow . Children ( ) . Append ( keyboardRemapControlObjects [ keyboardRemapControlObjects . size ( ) - 1 ] [ 1 ] - > getSingleKeyRemapControl ( ) ) ;
2020-03-27 23:38:58 +08:00
// Set the key text if the two keys are not null (i.e. default args)
if ( originalKey ! = NULL & & newKey ! = NULL )
{
2020-04-10 00:20:19 +08:00
singleKeyRemapBuffer . push_back ( std : : vector < DWORD > { originalKey , newKey } ) ;
2020-04-19 07:12:26 +08:00
std : : vector < DWORD > keyCodes = keyboardManagerState - > keyboardMap . GetKeyCodeList ( ) ;
auto it = std : : find ( keyCodes . begin ( ) , keyCodes . end ( ) , originalKey ) ;
if ( it ! = keyCodes . end ( ) )
{
keyboardRemapControlObjects [ keyboardRemapControlObjects . size ( ) - 1 ] [ 0 ] - > singleKeyRemapDropDown . SetSelectedIndex ( ( int32_t ) std : : distance ( keyCodes . begin ( ) , it ) ) ;
}
it = std : : find ( keyCodes . begin ( ) , keyCodes . end ( ) , newKey ) ;
if ( it ! = keyCodes . end ( ) )
{
keyboardRemapControlObjects [ keyboardRemapControlObjects . size ( ) - 1 ] [ 1 ] - > singleKeyRemapDropDown . SetSelectedIndex ( ( int32_t ) std : : distance ( keyCodes . begin ( ) , it ) ) ;
}
2020-04-10 00:20:19 +08:00
}
else
{
// Initialize both keys to NULL
singleKeyRemapBuffer . push_back ( std : : vector < DWORD > { NULL , NULL } ) ;
2020-03-27 23:38:58 +08:00
}
// Delete row button
Windows : : UI : : Xaml : : Controls : : Button deleteRemapKeys ;
deleteRemapKeys . Background ( Windows : : UI : : Xaml : : Media : : SolidColorBrush { Windows : : UI : : Colors : : LightGray ( ) } ) ;
deleteRemapKeys . Foreground ( Windows : : UI : : Xaml : : Media : : SolidColorBrush { Windows : : UI : : Colors : : Black ( ) } ) ;
FontIcon deleteSymbol ;
deleteSymbol . FontFamily ( Xaml : : Media : : FontFamily ( L " Segoe MDL2 Assets " ) ) ;
deleteSymbol . Glyph ( L " \xE74D " ) ;
deleteRemapKeys . Content ( deleteSymbol ) ;
deleteRemapKeys . Click ( [ & ] ( IInspectable const & sender , RoutedEventArgs const & ) {
StackPanel currentRow = sender . as < Button > ( ) . Parent ( ) . as < StackPanel > ( ) ;
uint32_t index ;
parent . Children ( ) . IndexOf ( currentRow , index ) ;
parent . Children ( ) . RemoveAt ( index ) ;
2020-04-10 00:20:19 +08:00
// delete the row from the buffer. Since first child of the stackpanel is the header, the effective index starts from 1
singleKeyRemapBuffer . erase ( singleKeyRemapBuffer . begin ( ) + ( index - 1 ) ) ;
2020-04-19 07:12:26 +08:00
// delete the SingleKeyRemapControl objects so that they get destructed
keyboardRemapControlObjects . erase ( keyboardRemapControlObjects . begin ( ) + ( index - 1 ) ) ;
2020-03-27 23:38:58 +08:00
} ) ;
tableRow . Children ( ) . Append ( deleteRemapKeys ) ;
parent . Children ( ) . Append ( tableRow ) ;
}
// Function to return the stack panel element of the SingleKeyRemapControl. This is the externally visible UI element which can be used to add it to other layouts
StackPanel SingleKeyRemapControl : : getSingleKeyRemapControl ( )
{
return singleKeyRemapControlLayout ;
}
// Function to create the detect remap key UI window
2020-04-19 07:12:26 +08:00
void SingleKeyRemapControl : : createDetectKeyWindow ( IInspectable const & sender , XamlRoot xamlRoot , std : : vector < std : : vector < DWORD > > & singleKeyRemapBuffer , KeyboardManagerState & keyboardManagerState , const size_t rowIndex , const size_t colIndex )
2020-03-27 23:38:58 +08:00
{
// ContentDialog for detecting remap key. This is the parent UI element.
ContentDialog detectRemapKeyBox ;
2020-04-17 00:16:48 +08:00
2020-04-09 05:31:31 +08:00
// TODO: Hardcoded light theme, since the app is not theme aware ATM.
detectRemapKeyBox . RequestedTheme ( ElementTheme : : Light ) ;
2020-03-27 23:38:58 +08:00
// ContentDialog requires manually setting the XamlRoot (https://docs.microsoft.com/en-us/uwp/api/windows.ui.xaml.controls.contentdialog#contentdialog-in-appwindow-or-xaml-islands)
detectRemapKeyBox . XamlRoot ( xamlRoot ) ;
detectRemapKeyBox . Title ( box_value ( L " Press a key on selected keyboard: " ) ) ;
2020-04-17 00:16:48 +08:00
detectRemapKeyBox . IsPrimaryButtonEnabled ( false ) ;
2020-03-27 23:38:58 +08:00
detectRemapKeyBox . IsSecondaryButtonEnabled ( false ) ;
// Get the linked text block for the "Type Key" button that was clicked
2020-04-19 07:12:26 +08:00
ComboBox linkedRemapDropDown = getSiblingElement ( sender ) . as < ComboBox > ( ) ;
2020-03-27 23:38:58 +08:00
2020-04-17 00:16:48 +08:00
auto unregisterKeys = [ & keyboardManagerState ] ( ) {
std : : thread t1 ( & KeyboardManagerState : : UnregisterKeyDelay , & keyboardManagerState , VK_ESCAPE ) ;
std : : thread t2 ( & KeyboardManagerState : : UnregisterKeyDelay , & keyboardManagerState , VK_RETURN ) ;
t1 . detach ( ) ;
t2 . detach ( ) ;
} ;
2020-04-19 07:12:26 +08:00
auto onAccept = [ linkedRemapDropDown ,
2020-04-17 00:16:48 +08:00
detectRemapKeyBox ,
& keyboardManagerState ,
& singleKeyRemapBuffer ,
unregisterKeys ,
rowIndex ,
colIndex ] {
2020-03-27 23:38:58 +08:00
// Save the detected key in the linked text block
DWORD detectedKey = keyboardManagerState . GetDetectedSingleRemapKey ( ) ;
2020-04-10 00:20:19 +08:00
2020-03-27 23:38:58 +08:00
if ( detectedKey ! = NULL )
{
2020-04-19 07:12:26 +08:00
std : : vector < DWORD > keyCodeList = keyboardManagerState . keyboardMap . GetKeyCodeList ( ) ;
// Update the drop down list with the new language to ensure that the correct key is displayed
linkedRemapDropDown . ItemsSource ( keyboardManagerState . keyboardMap . GetKeyNameList ( ) ) ;
auto it = std : : find ( keyCodeList . begin ( ) , keyCodeList . end ( ) , detectedKey ) ;
if ( it ! = keyCodeList . end ( ) )
{
linkedRemapDropDown . SelectedIndex ( ( int32_t ) std : : distance ( keyCodeList . begin ( ) , it ) ) ;
}
2020-03-27 23:38:58 +08:00
}
// Reset the keyboard manager UI state
keyboardManagerState . ResetUIState ( ) ;
2020-04-17 00:16:48 +08:00
unregisterKeys ( ) ;
detectRemapKeyBox . Hide ( ) ;
} ;
TextBlock primaryButtonText ;
primaryButtonText . Text ( to_hstring ( L " OK " ) ) ;
Button primaryButton ;
primaryButton . HorizontalAlignment ( HorizontalAlignment : : Stretch ) ;
primaryButton . Margin ( { 2 , 2 , 2 , 2 } ) ;
primaryButton . Content ( primaryButtonText ) ;
primaryButton . Click ( [ onAccept ] ( IInspectable const & sender , RoutedEventArgs const & ) {
onAccept ( ) ;
2020-03-27 23:38:58 +08:00
} ) ;
2020-04-17 00:16:48 +08:00
keyboardManagerState . RegisterKeyDelay (
VK_RETURN ,
std : : bind ( & KeyboardManagerState : : SelectDetectedRemapKey , & keyboardManagerState , std : : placeholders : : _1 ) ,
[ primaryButton , detectRemapKeyBox ] ( DWORD ) {
detectRemapKeyBox . Dispatcher ( ) . RunAsync (
Windows : : UI : : Core : : CoreDispatcherPriority : : Normal ,
[ primaryButton ] {
primaryButton . Background ( Windows : : UI : : Xaml : : Media : : SolidColorBrush { Windows : : UI : : Colors : : DarkGray ( ) } ) ;
} ) ;
} ,
[ onAccept , detectRemapKeyBox ] ( DWORD ) {
detectRemapKeyBox . Dispatcher ( ) . RunAsync (
Windows : : UI : : Core : : CoreDispatcherPriority : : Normal ,
[ onAccept ] {
onAccept ( ) ;
} ) ;
} ) ;
TextBlock cancelButtonText ;
cancelButtonText . Text ( to_hstring ( L " Cancel " ) ) ;
Button cancelButton ;
cancelButton . HorizontalAlignment ( HorizontalAlignment : : Stretch ) ;
cancelButton . Margin ( { 2 , 2 , 2 , 2 } ) ;
cancelButton . Content ( cancelButtonText ) ;
2020-03-27 23:38:58 +08:00
// Cancel button
2020-04-17 00:16:48 +08:00
cancelButton . Click ( [ detectRemapKeyBox , unregisterKeys , & keyboardManagerState ] ( IInspectable const & sender , RoutedEventArgs const & ) {
2020-03-27 23:38:58 +08:00
// Reset the keyboard manager UI state
keyboardManagerState . ResetUIState ( ) ;
2020-04-17 00:16:48 +08:00
unregisterKeys ( ) ;
detectRemapKeyBox . Hide ( ) ;
2020-03-27 23:38:58 +08:00
} ) ;
2020-04-17 00:16:48 +08:00
keyboardManagerState . RegisterKeyDelay (
VK_ESCAPE ,
std : : bind ( & KeyboardManagerState : : SelectDetectedRemapKey , & keyboardManagerState , std : : placeholders : : _1 ) ,
[ & keyboardManagerState , detectRemapKeyBox , unregisterKeys ] ( DWORD ) {
detectRemapKeyBox . Dispatcher ( ) . RunAsync (
Windows : : UI : : Core : : CoreDispatcherPriority : : Normal ,
[ detectRemapKeyBox ] {
detectRemapKeyBox . Hide ( ) ;
} ) ;
keyboardManagerState . ResetUIState ( ) ;
unregisterKeys ( ) ;
} ,
nullptr ) ;
2020-03-27 23:38:58 +08:00
// StackPanel parent for the displayed text in the dialog
Windows : : UI : : Xaml : : Controls : : StackPanel stackPanel ;
2020-04-09 05:31:31 +08:00
detectRemapKeyBox . Content ( stackPanel ) ;
2020-03-27 23:38:58 +08:00
// Header textblock
TextBlock text ;
text . Text ( winrt : : to_hstring ( " Key Pressed: " ) ) ;
text . Margin ( { 0 , 0 , 0 , 10 } ) ;
stackPanel . Children ( ) . Append ( text ) ;
2020-04-09 05:31:31 +08:00
// Target StackPanel to place the selected key
Windows : : UI : : Xaml : : Controls : : StackPanel keyStackPanel ;
keyStackPanel . Orientation ( Orientation : : Horizontal ) ;
2020-04-17 00:16:48 +08:00
stackPanel . Children ( ) . Append ( keyStackPanel ) ;
TextBlock holdEscInfo ;
holdEscInfo . Text ( winrt : : to_hstring ( " Hold Esc to discard " ) ) ;
holdEscInfo . FontSize ( 12 ) ;
holdEscInfo . Margin ( { 0 , 20 , 0 , 0 } ) ;
stackPanel . Children ( ) . Append ( holdEscInfo ) ;
TextBlock holdEnterInfo ;
holdEnterInfo . Text ( winrt : : to_hstring ( " Hold Enter to apply " ) ) ;
holdEnterInfo . FontSize ( 12 ) ;
holdEnterInfo . Margin ( { 0 , 0 , 0 , 0 } ) ;
stackPanel . Children ( ) . Append ( holdEnterInfo ) ;
ColumnDefinition primaryButtonColumn ;
ColumnDefinition cancelButtonColumn ;
Grid buttonPanel ;
buttonPanel . Margin ( { 0 , 20 , 0 , 0 } ) ;
buttonPanel . HorizontalAlignment ( HorizontalAlignment : : Stretch ) ;
buttonPanel . ColumnDefinitions ( ) . Append ( primaryButtonColumn ) ;
buttonPanel . ColumnDefinitions ( ) . Append ( cancelButtonColumn ) ;
buttonPanel . SetColumn ( primaryButton , 0 ) ;
buttonPanel . SetColumn ( cancelButton , 1 ) ;
buttonPanel . Children ( ) . Append ( primaryButton ) ;
buttonPanel . Children ( ) . Append ( cancelButton ) ;
stackPanel . Children ( ) . Append ( buttonPanel ) ;
2020-03-27 23:38:58 +08:00
stackPanel . UpdateLayout ( ) ;
// Configure the keyboardManagerState to store the UI information.
2020-04-09 05:31:31 +08:00
keyboardManagerState . ConfigureDetectSingleKeyRemapUI ( keyStackPanel ) ;
2020-03-27 23:38:58 +08:00
// Show the dialog
detectRemapKeyBox . ShowAsync ( ) ;
}