2021-04-26 22:01:38 +03:00
# include "pch.h"
# include "ShortcutControl.h"
# include <common/interop/shared_constants.h>
2021-05-07 11:16:31 +03:00
# include "KeyboardManagerState.h"
# include "KeyboardManagerEditorStrings.h"
# include "KeyDropDownControl.h"
# include "UIHelpers.h"
# include "EditorHelpers.h"
# include "EditorConstants.h"
2021-04-26 22:01:38 +03:00
//Both static members are initialized to null
HWND ShortcutControl : : editShortcutsWindowHandle = nullptr ;
2021-05-07 11:16:31 +03:00
KBMEditor : : KeyboardManagerState * ShortcutControl : : keyboardManagerState = nullptr ;
2021-04-26 22:01:38 +03:00
// Initialized as new vector
RemapBuffer ShortcutControl : : shortcutRemapBuffer ;
ShortcutControl : : ShortcutControl ( StackPanel table , StackPanel row , const int colIndex , TextBox targetApp )
{
shortcutDropDownStackPanel = StackPanel ( ) ;
typeShortcut = Button ( ) ;
shortcutControlLayout = StackPanel ( ) ;
bool isHybridControl = colIndex = = 1 ? true : false ;
2021-05-07 11:16:31 +03:00
shortcutDropDownStackPanel . as < StackPanel > ( ) . Spacing ( EditorConstants : : ShortcutTableDropDownSpacing ) ;
2021-04-26 22:01:38 +03:00
shortcutDropDownStackPanel . as < StackPanel > ( ) . Orientation ( Windows : : UI : : Xaml : : Controls : : Orientation : : Horizontal ) ;
typeShortcut . as < Button > ( ) . Content ( winrt : : box_value ( GET_RESOURCE_STRING ( IDS_TYPE_BUTTON ) ) ) ;
2021-05-07 11:16:31 +03:00
typeShortcut . as < Button > ( ) . Width ( EditorConstants : : ShortcutTableDropDownWidth ) ;
2021-04-26 22:01:38 +03:00
typeShortcut . as < Button > ( ) . Click ( [ & , table , row , colIndex , isHybridControl , targetApp ] ( winrt : : Windows : : Foundation : : IInspectable const & sender , RoutedEventArgs const & ) {
2021-05-07 11:16:31 +03:00
keyboardManagerState - > SetUIState ( KBMEditor : : KeyboardManagerUIState : : DetectShortcutWindowActivated , editShortcutsWindowHandle ) ;
2021-04-26 22:01:38 +03:00
// Using the XamlRoot of the typeShortcut to get the root of the XAML host
CreateDetectShortcutWindow ( sender , sender . as < Button > ( ) . XamlRoot ( ) , * keyboardManagerState , colIndex , table , keyDropDownControlObjects , row , targetApp , isHybridControl , false , editShortcutsWindowHandle , shortcutRemapBuffer ) ;
} ) ;
// Set an accessible name for the type shortcut button
typeShortcut . as < Button > ( ) . SetValue ( Automation : : AutomationProperties : : NameProperty ( ) , box_value ( GET_RESOURCE_STRING ( IDS_TYPE_BUTTON ) ) ) ;
2021-05-07 11:16:31 +03:00
shortcutControlLayout . as < StackPanel > ( ) . Spacing ( EditorConstants : : ShortcutTableDropDownSpacing ) ;
2021-04-26 22:01:38 +03:00
shortcutControlLayout . as < StackPanel > ( ) . Children ( ) . Append ( typeShortcut . as < Button > ( ) ) ;
shortcutControlLayout . as < StackPanel > ( ) . Children ( ) . Append ( shortcutDropDownStackPanel . as < StackPanel > ( ) ) ;
KeyDropDownControl : : AddDropDown ( table , row , shortcutDropDownStackPanel . as < StackPanel > ( ) , colIndex , shortcutRemapBuffer , keyDropDownControlObjects , targetApp , isHybridControl , false ) ;
2021-10-06 15:54:15 +01:00
try
{
// If a layout update has been triggered by other methods (e.g.: adapting to zoom level), this may throw an exception.
shortcutControlLayout . as < StackPanel > ( ) . UpdateLayout ( ) ;
}
catch ( . . . )
{
}
2021-04-26 22:01:38 +03:00
}
// Function to set the accessible name of the target App text box
void ShortcutControl : : SetAccessibleNameForTextBox ( TextBox targetAppTextBox , int rowIndex )
{
// To set the accessible name of the target App text box by adding the string `All Apps` if the text box is empty, if not the application name is read by narrator.
std : : wstring targetAppTextBoxAccessibleName = GET_RESOURCE_STRING ( IDS_AUTOMATIONPROPERTIES_ROW ) + std : : to_wstring ( rowIndex ) + L " , " + GET_RESOURCE_STRING ( IDS_EDITSHORTCUTS_TARGETAPPHEADER ) ;
if ( targetAppTextBox . Text ( ) = = L " " )
{
targetAppTextBoxAccessibleName + = GET_RESOURCE_STRING ( IDS_EDITSHORTCUTS_ALLAPPS ) ;
}
targetAppTextBox . SetValue ( Automation : : AutomationProperties : : NameProperty ( ) , box_value ( targetAppTextBoxAccessibleName ) ) ;
}
// Function to set the accessible names for all the controls in a row
void ShortcutControl : : UpdateAccessibleNames ( StackPanel sourceColumn , StackPanel mappedToColumn , TextBox targetAppTextBox , Button deleteButton , int rowIndex )
{
sourceColumn . SetValue ( Automation : : AutomationProperties : : NameProperty ( ) , box_value ( GET_RESOURCE_STRING ( IDS_AUTOMATIONPROPERTIES_ROW ) + std : : to_wstring ( rowIndex ) + L " , " + GET_RESOURCE_STRING ( IDS_EDITSHORTCUTS_SOURCEHEADER ) ) ) ;
mappedToColumn . SetValue ( Automation : : AutomationProperties : : NameProperty ( ) , box_value ( GET_RESOURCE_STRING ( IDS_AUTOMATIONPROPERTIES_ROW ) + std : : to_wstring ( rowIndex ) + L " , " + GET_RESOURCE_STRING ( IDS_EDITSHORTCUTS_TARGETHEADER ) ) ) ;
ShortcutControl : : SetAccessibleNameForTextBox ( targetAppTextBox , rowIndex ) ;
deleteButton . SetValue ( Automation : : AutomationProperties : : NameProperty ( ) , box_value ( GET_RESOURCE_STRING ( IDS_AUTOMATIONPROPERTIES_ROW ) + std : : to_wstring ( rowIndex ) + L " , " + GET_RESOURCE_STRING ( IDS_DELETE_REMAPPING_BUTTON ) ) ) ;
}
// Function to add a new row to the shortcut table. If the originalKeys and newKeys args are provided, then the displayed shortcuts are set to those values.
void ShortcutControl : : AddNewShortcutControlRow ( StackPanel & parent , std : : vector < std : : vector < std : : unique_ptr < ShortcutControl > > > & keyboardRemapControlObjects , const Shortcut & originalKeys , const KeyShortcutUnion & newKeys , const std : : wstring & targetAppName )
{
// Textbox for target application
TextBox targetAppTextBox ;
// Create new ShortcutControl objects dynamically so that we does not get destructed
std : : vector < std : : unique_ptr < ShortcutControl > > newrow ;
StackPanel row = StackPanel ( ) ;
parent . Children ( ) . Append ( row ) ;
newrow . emplace_back ( std : : make_unique < ShortcutControl > ( parent , row , 0 , targetAppTextBox ) ) ;
newrow . emplace_back ( std : : make_unique < ShortcutControl > ( parent , row , 1 , targetAppTextBox ) ) ;
keyboardRemapControlObjects . push_back ( std : : move ( newrow ) ) ;
row . Padding ( { 10 , 10 , 10 , 10 } ) ;
row . Orientation ( Orientation : : Horizontal ) ;
auto brush = Windows : : UI : : Xaml : : Application : : Current ( ) . Resources ( ) . Lookup ( box_value ( L " SystemControlBackgroundListLowBrush " ) ) . as < Windows : : UI : : Xaml : : Media : : SolidColorBrush > ( ) ;
if ( keyboardRemapControlObjects . size ( ) % 2 )
{
row . Background ( brush ) ;
}
// ShortcutControl for the original shortcut
auto origin = keyboardRemapControlObjects . back ( ) [ 0 ] - > GetShortcutControl ( ) ;
2021-05-07 11:16:31 +03:00
origin . Width ( EditorConstants : : ShortcutOriginColumnWidth ) ;
2021-04-26 22:01:38 +03:00
row . Children ( ) . Append ( origin ) ;
// Arrow icon
FontIcon arrowIcon ;
arrowIcon . FontFamily ( Xaml : : Media : : FontFamily ( L " Segoe MDL2 Assets " ) ) ;
arrowIcon . Glyph ( L " \xE72A " ) ;
arrowIcon . VerticalAlignment ( VerticalAlignment : : Center ) ;
arrowIcon . HorizontalAlignment ( HorizontalAlignment : : Center ) ;
2021-05-07 11:16:31 +03:00
auto arrowIconContainer = UIHelpers : : GetWrapped ( arrowIcon , EditorConstants : : ShortcutArrowColumnWidth ) . as < StackPanel > ( ) ;
2021-04-26 22:01:38 +03:00
arrowIconContainer . Orientation ( Orientation : : Vertical ) ;
arrowIconContainer . VerticalAlignment ( VerticalAlignment : : Center ) ;
row . Children ( ) . Append ( arrowIconContainer ) ;
// ShortcutControl for the new shortcut
auto target = keyboardRemapControlObjects . back ( ) [ 1 ] - > GetShortcutControl ( ) ;
2021-05-07 11:16:31 +03:00
target . Width ( EditorConstants : : ShortcutTargetColumnWidth ) ;
2021-04-26 22:01:38 +03:00
row . Children ( ) . Append ( target ) ;
2021-05-07 11:16:31 +03:00
targetAppTextBox . Width ( EditorConstants : : ShortcutTableDropDownWidth ) ;
2021-04-26 22:01:38 +03:00
targetAppTextBox . PlaceholderText ( KeyboardManagerEditorStrings : : DefaultAppName ) ;
targetAppTextBox . Text ( targetAppName ) ;
// GotFocus handler will be called whenever the user tabs into or clicks on the textbox
targetAppTextBox . GotFocus ( [ targetAppTextBox ] ( auto const & sender , auto const & e ) {
// Select all text for accessible purpose
targetAppTextBox . SelectAll ( ) ;
} ) ;
// LostFocus handler will be called whenever text is updated by a user and then they click something else or tab to another control. Does not get called if Text is updated while the TextBox isn't in focus (i.e. from code)
targetAppTextBox . LostFocus ( [ & keyboardRemapControlObjects , parent , row , targetAppTextBox ] ( auto const & sender , auto const & e ) {
// Get index of targetAppTextBox button
uint32_t rowIndex ;
if ( ! parent . Children ( ) . IndexOf ( row , rowIndex ) )
{
return ;
}
// rowIndex could be out of bounds if the the row got deleted after LostFocus handler was invoked. In this case it should return
if ( rowIndex > = keyboardRemapControlObjects . size ( ) )
{
return ;
}
// Validate both set of drop downs
KeyDropDownControl : : ValidateShortcutFromDropDownList ( parent , row , keyboardRemapControlObjects [ rowIndex ] [ 0 ] - > shortcutDropDownStackPanel . as < StackPanel > ( ) , 0 , ShortcutControl : : shortcutRemapBuffer , keyboardRemapControlObjects [ rowIndex ] [ 0 ] - > keyDropDownControlObjects , targetAppTextBox , false , false ) ;
KeyDropDownControl : : ValidateShortcutFromDropDownList ( parent , row , keyboardRemapControlObjects [ rowIndex ] [ 1 ] - > shortcutDropDownStackPanel . as < StackPanel > ( ) , 1 , ShortcutControl : : shortcutRemapBuffer , keyboardRemapControlObjects [ rowIndex ] [ 1 ] - > keyDropDownControlObjects , targetAppTextBox , true , false ) ;
// Reset the buffer based on the selected drop down items
std : : get < Shortcut > ( shortcutRemapBuffer [ rowIndex ] . first [ 0 ] ) . SetKeyCodes ( KeyDropDownControl : : GetSelectedCodesFromStackPanel ( keyboardRemapControlObjects [ rowIndex ] [ 0 ] - > shortcutDropDownStackPanel . as < StackPanel > ( ) ) ) ;
// second column is a hybrid column
std : : vector < int32_t > selectedKeyCodes = KeyDropDownControl : : GetSelectedCodesFromStackPanel ( keyboardRemapControlObjects [ rowIndex ] [ 1 ] - > shortcutDropDownStackPanel . as < StackPanel > ( ) ) ;
// If exactly one key is selected consider it to be a key remap
if ( selectedKeyCodes . size ( ) = = 1 )
{
2021-06-01 19:15:16 +03:00
shortcutRemapBuffer [ rowIndex ] . first [ 1 ] = ( DWORD ) selectedKeyCodes [ 0 ] ;
2021-04-26 22:01:38 +03:00
}
else
{
Shortcut tempShortcut ;
tempShortcut . SetKeyCodes ( selectedKeyCodes ) ;
// Assign instead of setting the value in the buffer since the previous value may not be a Shortcut
shortcutRemapBuffer [ rowIndex ] . first [ 1 ] = tempShortcut ;
}
std : : wstring newText = targetAppTextBox . Text ( ) . c_str ( ) ;
std : : wstring lowercaseDefAppName = KeyboardManagerEditorStrings : : DefaultAppName ;
std : : transform ( newText . begin ( ) , newText . end ( ) , newText . begin ( ) , towlower ) ;
std : : transform ( lowercaseDefAppName . begin ( ) , lowercaseDefAppName . end ( ) , lowercaseDefAppName . begin ( ) , towlower ) ;
if ( newText = = lowercaseDefAppName )
{
shortcutRemapBuffer [ rowIndex ] . second = L " " ;
}
else
{
shortcutRemapBuffer [ rowIndex ] . second = targetAppTextBox . Text ( ) . c_str ( ) ;
}
2022-03-17 13:19:16 -04:00
// To set the accessible name of the target app text box when focus is lost
2021-04-26 22:01:38 +03:00
ShortcutControl : : SetAccessibleNameForTextBox ( targetAppTextBox , rowIndex + 1 ) ;
} ) ;
// We need two containers in order to align it horizontally and vertically
2021-05-07 11:16:31 +03:00
StackPanel targetAppHorizontal = UIHelpers : : GetWrapped ( targetAppTextBox , EditorConstants : : TableTargetAppColWidth ) . as < StackPanel > ( ) ;
2021-04-26 22:01:38 +03:00
targetAppHorizontal . Orientation ( Orientation : : Horizontal ) ;
targetAppHorizontal . HorizontalAlignment ( HorizontalAlignment : : Left ) ;
2021-05-07 11:16:31 +03:00
StackPanel targetAppContainer = UIHelpers : : GetWrapped ( targetAppHorizontal , EditorConstants : : TableTargetAppColWidth ) . as < StackPanel > ( ) ;
2021-04-26 22:01:38 +03:00
targetAppContainer . Orientation ( Orientation : : Vertical ) ;
targetAppContainer . VerticalAlignment ( VerticalAlignment : : Bottom ) ;
row . Children ( ) . Append ( targetAppContainer ) ;
// Delete row button
Windows : : UI : : Xaml : : Controls : : Button deleteShortcut ;
FontIcon deleteSymbol ;
deleteSymbol . FontFamily ( Xaml : : Media : : FontFamily ( L " Segoe MDL2 Assets " ) ) ;
deleteSymbol . Glyph ( L " \xE74D " ) ;
deleteShortcut . Content ( deleteSymbol ) ;
deleteShortcut . Background ( Media : : SolidColorBrush ( Colors : : Transparent ( ) ) ) ;
deleteShortcut . HorizontalAlignment ( HorizontalAlignment : : Center ) ;
2021-07-29 10:37:09 +02:00
deleteShortcut . Click ( [ & , parent , row , brush , deleteShortcut ] ( winrt : : Windows : : Foundation : : IInspectable const & sender , RoutedEventArgs const & ) {
2021-04-26 22:01:38 +03:00
Button currentButton = sender . as < Button > ( ) ;
uint32_t rowIndex ;
// Get index of delete button
UIElementCollection children = parent . Children ( ) ;
bool indexFound = children . IndexOf ( row , rowIndex ) ;
// IndexOf could fail if the the row got deleted and the button handler was invoked twice. In this case it should return
if ( ! indexFound )
{
return ;
}
for ( uint32_t i = rowIndex + 1 ; i < children . Size ( ) ; i + + )
{
StackPanel row = children . GetAt ( i ) . as < StackPanel > ( ) ;
row . Background ( i % 2 ? brush : Media : : SolidColorBrush ( Colors : : Transparent ( ) ) ) ;
StackPanel sourceCol = row . Children ( ) . GetAt ( 0 ) . as < StackPanel > ( ) ;
StackPanel targetCol = row . Children ( ) . GetAt ( 2 ) . as < StackPanel > ( ) ;
TextBox targetApp = row . Children ( ) . GetAt ( 3 ) . as < StackPanel > ( ) . Children ( ) . GetAt ( 0 ) . as < StackPanel > ( ) . Children ( ) . GetAt ( 0 ) . as < TextBox > ( ) ;
Button delButton = row . Children ( ) . GetAt ( 4 ) . as < StackPanel > ( ) . Children ( ) . GetAt ( 0 ) . as < Button > ( ) ;
UpdateAccessibleNames ( sourceCol , targetCol , targetApp , delButton , i ) ;
}
2021-07-29 10:37:09 +02:00
if ( auto automationPeer { Automation : : Peers : : FrameworkElementAutomationPeer : : FromElement ( deleteShortcut ) } )
{
automationPeer . RaiseNotificationEvent (
Automation : : Peers : : AutomationNotificationKind : : ActionCompleted ,
Automation : : Peers : : AutomationNotificationProcessing : : ImportantMostRecent ,
GET_RESOURCE_STRING ( IDS_DELETE_REMAPPING_EVENT ) ,
L " ShortcutRemappingDeletedNotificationEvent " /* unique name for this notification category */ ) ;
}
2021-04-26 22:01:38 +03:00
children . RemoveAt ( rowIndex ) ;
shortcutRemapBuffer . erase ( shortcutRemapBuffer . begin ( ) + rowIndex ) ;
// delete the SingleKeyRemapControl objects so that they get destructed
keyboardRemapControlObjects . erase ( keyboardRemapControlObjects . begin ( ) + rowIndex ) ;
} ) ;
// To set the accessible name of the delete button
deleteShortcut . SetValue ( Automation : : AutomationProperties : : NameProperty ( ) , box_value ( GET_RESOURCE_STRING ( IDS_DELETE_REMAPPING_BUTTON ) ) ) ;
// Add tooltip for delete button which would appear on hover
ToolTip deleteShortcuttoolTip ;
deleteShortcuttoolTip . Content ( box_value ( GET_RESOURCE_STRING ( IDS_DELETE_REMAPPING_BUTTON ) ) ) ;
ToolTipService : : SetToolTip ( deleteShortcut , deleteShortcuttoolTip ) ;
StackPanel deleteShortcutContainer = StackPanel ( ) ;
deleteShortcutContainer . Children ( ) . Append ( deleteShortcut ) ;
deleteShortcutContainer . Orientation ( Orientation : : Vertical ) ;
deleteShortcutContainer . VerticalAlignment ( VerticalAlignment : : Center ) ;
row . Children ( ) . Append ( deleteShortcutContainer ) ;
// Set accessible names
UpdateAccessibleNames ( keyboardRemapControlObjects [ keyboardRemapControlObjects . size ( ) - 1 ] [ 0 ] - > GetShortcutControl ( ) , keyboardRemapControlObjects [ keyboardRemapControlObjects . size ( ) - 1 ] [ 1 ] - > GetShortcutControl ( ) , targetAppTextBox , deleteShortcut , ( int ) keyboardRemapControlObjects . size ( ) ) ;
// Set the shortcut text if the two vectors are not empty (i.e. default args)
2021-05-07 11:16:31 +03:00
if ( EditorHelpers : : IsValidShortcut ( originalKeys ) & & ! ( newKeys . index ( ) = = 0 & & std : : get < DWORD > ( newKeys ) = = NULL ) & & ! ( newKeys . index ( ) = = 1 & & ! EditorHelpers : : IsValidShortcut ( std : : get < Shortcut > ( newKeys ) ) ) )
2021-04-26 22:01:38 +03:00
{
// change to load app name
shortcutRemapBuffer . push_back ( std : : make_pair < RemapBufferItem , std : : wstring > ( RemapBufferItem { Shortcut ( ) , Shortcut ( ) } , std : : wstring ( targetAppName ) ) ) ;
KeyDropDownControl : : AddShortcutToControl ( originalKeys , parent , keyboardRemapControlObjects [ keyboardRemapControlObjects . size ( ) - 1 ] [ 0 ] - > shortcutDropDownStackPanel . as < StackPanel > ( ) , * keyboardManagerState , 0 , keyboardRemapControlObjects [ keyboardRemapControlObjects . size ( ) - 1 ] [ 0 ] - > keyDropDownControlObjects , shortcutRemapBuffer , row , targetAppTextBox , false , false ) ;
if ( newKeys . index ( ) = = 0 )
{
keyboardRemapControlObjects [ keyboardRemapControlObjects . size ( ) - 1 ] [ 1 ] - > keyDropDownControlObjects [ 0 ] - > SetSelectedValue ( std : : to_wstring ( std : : get < DWORD > ( newKeys ) ) ) ;
}
else
{
KeyDropDownControl : : AddShortcutToControl ( std : : get < Shortcut > ( newKeys ) , parent , keyboardRemapControlObjects . back ( ) [ 1 ] - > shortcutDropDownStackPanel . as < StackPanel > ( ) , * keyboardManagerState , 1 , keyboardRemapControlObjects [ keyboardRemapControlObjects . size ( ) - 1 ] [ 1 ] - > keyDropDownControlObjects , shortcutRemapBuffer , row , targetAppTextBox , true , false ) ;
}
}
else
{
// Initialize both shortcuts as empty shortcuts
shortcutRemapBuffer . push_back ( std : : make_pair < RemapBufferItem , std : : wstring > ( RemapBufferItem { Shortcut ( ) , Shortcut ( ) } , std : : wstring ( targetAppName ) ) ) ;
}
}
// Function to return the stack panel element of the ShortcutControl. This is the externally visible UI element which can be used to add it to other layouts
StackPanel ShortcutControl : : GetShortcutControl ( )
{
return shortcutControlLayout . as < StackPanel > ( ) ;
}
// Function to create the detect shortcut UI window
2021-05-07 11:16:31 +03:00
void ShortcutControl : : CreateDetectShortcutWindow ( winrt : : Windows : : Foundation : : IInspectable const & sender , XamlRoot xamlRoot , KBMEditor : : KeyboardManagerState & keyboardManagerState , const int colIndex , StackPanel table , std : : vector < std : : unique_ptr < KeyDropDownControl > > & keyDropDownControlObjects , StackPanel row , TextBox targetApp , bool isHybridControl , bool isSingleKeyWindow , HWND parentWindow , RemapBuffer & remapBuffer )
2021-04-26 22:01:38 +03:00
{
// ContentDialog for detecting shortcuts. This is the parent UI element.
ContentDialog detectShortcutBox ;
// 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)
detectShortcutBox . XamlRoot ( xamlRoot ) ;
detectShortcutBox . Title ( box_value ( GET_RESOURCE_STRING ( IDS_TYPESHORTCUT_TITLE ) ) ) ;
detectShortcutBox . IsPrimaryButtonEnabled ( false ) ;
detectShortcutBox . IsSecondaryButtonEnabled ( false ) ;
// Get the linked stack panel for the "Type shortcut" button that was clicked
StackPanel linkedShortcutStackPanel = UIHelpers : : GetSiblingElement ( sender ) . as < StackPanel > ( ) ;
auto unregisterKeys = [ & keyboardManagerState ] ( ) {
keyboardManagerState . ClearRegisteredKeyDelays ( ) ;
} ;
auto selectDetectedShortcutAndResetKeys = [ & keyboardManagerState ] ( DWORD key ) {
keyboardManagerState . SelectDetectedShortcut ( key ) ;
keyboardManagerState . ResetDetectedShortcutKey ( key ) ;
} ;
auto onPressEnter = [ linkedShortcutStackPanel ,
detectShortcutBox ,
& keyboardManagerState ,
unregisterKeys ,
colIndex ,
table ,
targetApp ,
& keyDropDownControlObjects ,
row ,
isHybridControl ,
isSingleKeyWindow ,
& remapBuffer ] {
// Save the detected shortcut in the linked text block
Shortcut detectedShortcutKeys = keyboardManagerState . GetDetectedShortcut ( ) ;
if ( ! detectedShortcutKeys . IsEmpty ( ) )
{
// The shortcut buffer gets set in this function
KeyDropDownControl : : AddShortcutToControl ( detectedShortcutKeys , table , linkedShortcutStackPanel , keyboardManagerState , colIndex , keyDropDownControlObjects , remapBuffer , row , targetApp , isHybridControl , isSingleKeyWindow ) ;
}
// Hide the type shortcut UI
detectShortcutBox . Hide ( ) ;
} ;
auto onReleaseEnter = [ & keyboardManagerState ,
unregisterKeys ,
isSingleKeyWindow ,
parentWindow ] {
// Reset the keyboard manager UI state
keyboardManagerState . ResetUIState ( ) ;
if ( isSingleKeyWindow )
{
// Revert UI state back to Edit Keyboard window
2021-05-07 11:16:31 +03:00
keyboardManagerState . SetUIState ( KBMEditor : : KeyboardManagerUIState : : EditKeyboardWindowActivated , parentWindow ) ;
2021-04-26 22:01:38 +03:00
}
else
{
// Revert UI state back to Edit Shortcut window
2021-05-07 11:16:31 +03:00
keyboardManagerState . SetUIState ( KBMEditor : : KeyboardManagerUIState : : EditShortcutsWindowActivated , parentWindow ) ;
2021-04-26 22:01:38 +03:00
}
unregisterKeys ( ) ;
} ;
auto onAccept = [ onPressEnter ,
onReleaseEnter ] {
onPressEnter ( ) ;
onReleaseEnter ( ) ;
} ;
TextBlock primaryButtonText ;
primaryButtonText . Text ( GET_RESOURCE_STRING ( IDS_OK_BUTTON ) ) ;
Button primaryButton ;
primaryButton . HorizontalAlignment ( HorizontalAlignment : : Stretch ) ;
primaryButton . Margin ( { 2 , 2 , 2 , 2 } ) ;
primaryButton . Content ( primaryButtonText ) ;
// OK button
primaryButton . Click ( [ onAccept ] ( winrt : : Windows : : Foundation : : IInspectable const & sender , RoutedEventArgs const & ) {
onAccept ( ) ;
} ) ;
// NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread
keyboardManagerState . RegisterKeyDelay (
VK_RETURN ,
selectDetectedShortcutAndResetKeys ,
[ primaryButton , onPressEnter , detectShortcutBox ] ( DWORD ) {
detectShortcutBox . Dispatcher ( ) . RunAsync (
Windows : : UI : : Core : : CoreDispatcherPriority : : Normal ,
[ primaryButton , onPressEnter ] {
// Use the base medium low brush to be consistent with the theme
primaryButton . Background ( Windows : : UI : : Xaml : : Application : : Current ( ) . Resources ( ) . Lookup ( box_value ( L " SystemControlBackgroundBaseMediumLowBrush " ) ) . as < Windows : : UI : : Xaml : : Media : : SolidColorBrush > ( ) ) ;
onPressEnter ( ) ;
} ) ;
} ,
[ onReleaseEnter , detectShortcutBox ] ( DWORD ) {
detectShortcutBox . Dispatcher ( ) . RunAsync (
Windows : : UI : : Core : : CoreDispatcherPriority : : Normal ,
[ onReleaseEnter ] ( ) {
onReleaseEnter ( ) ;
} ) ;
} ) ;
TextBlock cancelButtonText ;
cancelButtonText . Text ( GET_RESOURCE_STRING ( IDS_CANCEL_BUTTON ) ) ;
auto onCancel = [ & keyboardManagerState ,
detectShortcutBox ,
unregisterKeys ,
isSingleKeyWindow ,
parentWindow ] {
detectShortcutBox . Hide ( ) ;
// Reset the keyboard manager UI state
keyboardManagerState . ResetUIState ( ) ;
if ( isSingleKeyWindow )
{
// Revert UI state back to Edit Keyboard window
2021-05-07 11:16:31 +03:00
keyboardManagerState . SetUIState ( KBMEditor : : KeyboardManagerUIState : : EditKeyboardWindowActivated , parentWindow ) ;
2021-04-26 22:01:38 +03:00
}
else
{
// Revert UI state back to Edit Shortcut window
2021-05-07 11:16:31 +03:00
keyboardManagerState . SetUIState ( KBMEditor : : KeyboardManagerUIState : : EditShortcutsWindowActivated , parentWindow ) ;
2021-04-26 22:01:38 +03:00
}
unregisterKeys ( ) ;
} ;
Button cancelButton ;
cancelButton . HorizontalAlignment ( HorizontalAlignment : : Stretch ) ;
cancelButton . Margin ( { 2 , 2 , 2 , 2 } ) ;
cancelButton . Content ( cancelButtonText ) ;
// Cancel button
cancelButton . Click ( [ onCancel ] ( winrt : : Windows : : Foundation : : IInspectable const & sender , RoutedEventArgs const & ) {
onCancel ( ) ;
} ) ;
// NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread
keyboardManagerState . RegisterKeyDelay (
VK_ESCAPE ,
selectDetectedShortcutAndResetKeys ,
[ onCancel , detectShortcutBox ] ( DWORD ) {
detectShortcutBox . Dispatcher ( ) . RunAsync (
Windows : : UI : : Core : : CoreDispatcherPriority : : Normal ,
[ onCancel ] {
onCancel ( ) ;
} ) ;
} ,
nullptr ) ;
// StackPanel parent for the displayed text in the dialog
Windows : : UI : : Xaml : : Controls : : StackPanel stackPanel ;
detectShortcutBox . Content ( stackPanel ) ;
// Header textblock
TextBlock text ;
text . Text ( GET_RESOURCE_STRING ( IDS_TYPESHORTCUT_HEADER ) ) ;
text . Margin ( { 0 , 0 , 0 , 10 } ) ;
stackPanel . Children ( ) . Append ( text ) ;
// Target StackPanel to place the selected key - first row (for 1-3 keys)
Windows : : UI : : Xaml : : Controls : : StackPanel keyStackPanel1 ;
keyStackPanel1 . Orientation ( Orientation : : Horizontal ) ;
stackPanel . Children ( ) . Append ( keyStackPanel1 ) ;
// Target StackPanel to place the selected key - second row (for 4-5 keys)
Windows : : UI : : Xaml : : Controls : : StackPanel keyStackPanel2 ;
keyStackPanel2 . Orientation ( Orientation : : Horizontal ) ;
keyStackPanel2 . Margin ( { 0 , 20 , 0 , 0 } ) ;
keyStackPanel2 . Visibility ( Visibility : : Collapsed ) ;
stackPanel . Children ( ) . Append ( keyStackPanel2 ) ;
TextBlock holdEscInfo ;
holdEscInfo . Text ( GET_RESOURCE_STRING ( IDS_TYPE_HOLDESC ) ) ;
holdEscInfo . FontSize ( 12 ) ;
holdEscInfo . Margin ( { 0 , 20 , 0 , 0 } ) ;
stackPanel . Children ( ) . Append ( holdEscInfo ) ;
TextBlock holdEnterInfo ;
holdEnterInfo . Text ( GET_RESOURCE_STRING ( IDS_TYPE_HOLDENTER ) ) ;
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 ) ;
2021-10-06 15:54:15 +01:00
try
{
// If a layout update has been triggered by other methods (e.g.: adapting to zoom level), this may throw an exception.
stackPanel . UpdateLayout ( ) ;
}
catch ( . . . )
{
}
2021-04-26 22:01:38 +03:00
// Configure the keyboardManagerState to store the UI information.
keyboardManagerState . ConfigureDetectShortcutUI ( keyStackPanel1 , keyStackPanel2 ) ;
// Show the dialog
detectShortcutBox . ShowAsync ( ) ;
}