2019-03-21 16:05:16 +01:00
/ *
2022-01-21 16:33:52 +01:00
* Copyright ( c ) 2022 ETH Zürich , Educational Development and Technology ( LET )
2019-03-21 16:05:16 +01:00
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2.0 . If a copy of the MPL was not distributed with this
* file , You can obtain one at http : //mozilla.org/MPL/2.0/.
* /
using System ;
using System.Linq ;
using System.Threading.Tasks ;
2019-12-06 14:23:07 +01:00
using System.Windows ;
2022-09-22 11:28:33 +02:00
using System.Windows.Automation ;
2019-03-21 16:05:16 +01:00
using System.Windows.Controls ;
2019-12-06 14:23:07 +01:00
using System.Windows.Controls.Primitives ;
2019-03-21 16:05:16 +01:00
using System.Windows.Media ;
2019-08-30 15:59:51 +02:00
using SafeExamBrowser.I18n.Contracts ;
using SafeExamBrowser.SystemComponents.Contracts.Keyboard ;
2019-08-30 09:55:26 +02:00
using SafeExamBrowser.UserInterface.Contracts.Shell ;
2019-03-21 16:05:16 +01:00
2020-03-17 10:37:08 +01:00
namespace SafeExamBrowser.UserInterface.Mobile.Controls.Taskbar
2019-03-21 16:05:16 +01:00
{
2020-03-17 10:37:08 +01:00
internal partial class KeyboardLayoutControl : UserControl , ISystemControl
2019-03-21 16:05:16 +01:00
{
2019-08-30 15:59:51 +02:00
private IKeyboard keyboard ;
private IText text ;
2019-03-21 16:05:16 +01:00
2020-03-17 10:37:08 +01:00
internal KeyboardLayoutControl ( IKeyboard keyboard , IText text )
2019-03-21 16:05:16 +01:00
{
2019-08-30 15:59:51 +02:00
this . keyboard = keyboard ;
this . text = text ;
2019-03-21 16:05:16 +01:00
InitializeComponent ( ) ;
InitializeKeyboardLayoutControl ( ) ;
}
public void Close ( )
{
Dispatcher . Invoke ( ( ) = > Popup . IsOpen = false ) ;
}
private void InitializeKeyboardLayoutControl ( )
{
var originalBrush = Button . Background ;
2019-08-30 15:59:51 +02:00
InitializeLayouts ( ) ;
keyboard . LayoutChanged + = Keyboard_LayoutChanged ;
2022-05-18 10:51:58 +02:00
Button . Click + = ( o , args ) = >
{
Popup . IsOpen = ! Popup . IsOpen ;
Task . Delay ( 200 ) . ContinueWith ( _ = > this . Dispatcher . BeginInvoke ( ( System . Action ) ( ( ) = >
{
( ( LayoutsStackPanel . Children [ 0 ] as ContentControl ) . Content as UIElement ) . Focus ( ) ;
} ) ) ) ;
} ;
2023-02-13 13:39:53 +01:00
var lastOpenedBySpacePress = DateTime . MinValue ;
Button . PreviewKeyDown + = ( o , args ) = >
{
if ( args . Key = = System . Windows . Input . Key . Space ) // for some reason, the popup immediately closes again if opened by a Space Bar key event - as a mitigation, we record the space bar event and leave the popup open for at least 3 seconds
{
lastOpenedBySpacePress = DateTime . Now ;
}
} ;
Button . MouseLeave + = ( o , args ) = > Task . Delay ( 250 ) . ContinueWith ( _ = > Dispatcher . Invoke ( ( ) = >
{
if ( Popup . IsOpen & & ( DateTime . Now - lastOpenedBySpacePress ) . TotalSeconds < 3 )
{
return ;
}
Popup . IsOpen = Popup . IsMouseOver ;
} ) ) ;
2019-12-06 14:23:07 +01:00
Popup . CustomPopupPlacementCallback = new CustomPopupPlacementCallback ( Popup_PlacementCallback ) ;
2019-03-21 16:05:16 +01:00
Popup . MouseLeave + = ( o , args ) = > Task . Delay ( 250 ) . ContinueWith ( _ = > Dispatcher . Invoke ( ( ) = > Popup . IsOpen = IsMouseOver ) ) ;
Popup . Opened + = ( o , args ) = >
{
Background = Brushes . LightGray ;
Button . Background = Brushes . LightGray ;
} ;
Popup . Closed + = ( o , args ) = >
{
Background = originalBrush ;
Button . Background = originalBrush ;
} ;
}
2019-08-30 15:59:51 +02:00
private void Keyboard_LayoutChanged ( IKeyboardLayout layout )
{
Dispatcher . InvokeAsync ( ( ) = > SetCurrent ( layout ) ) ;
}
2019-12-06 14:23:07 +01:00
private CustomPopupPlacement [ ] Popup_PlacementCallback ( Size popupSize , Size targetSize , Point offset )
{
return new [ ]
{
new CustomPopupPlacement ( new Point ( targetSize . Width / 2 - popupSize . Width / 2 , - popupSize . Height ) , PopupPrimaryAxis . None )
} ;
}
2019-08-30 15:59:51 +02:00
private void InitializeLayouts ( )
{
foreach ( var layout in keyboard . GetLayouts ( ) )
{
2020-03-17 10:37:08 +01:00
var button = new KeyboardLayoutButton ( layout ) ;
2019-08-30 15:59:51 +02:00
button . LayoutSelected + = ( o , args ) = > ActivateLayout ( layout ) ;
LayoutsStackPanel . Children . Add ( button ) ;
if ( layout . IsCurrent )
{
SetCurrent ( layout ) ;
}
}
}
private void ActivateLayout ( IKeyboardLayout layout )
2019-03-21 16:05:16 +01:00
{
Popup . IsOpen = false ;
2019-08-30 15:59:51 +02:00
keyboard . ActivateLayout ( layout . Id ) ;
}
private void SetCurrent ( IKeyboardLayout layout )
{
2022-04-26 16:45:53 +02:00
var name = layout . CultureName ? . Length > 3 ? String . Join ( string . Empty , layout . CultureName . Split ( ' ' ) . Where ( s = > Char . IsLetter ( s . First ( ) ) ) . Select ( s = > s . First ( ) ) ) : layout . CultureName ;
var tooltip = text . Get ( TextKey . SystemControl_KeyboardLayoutTooltip ) . Replace ( "%%LAYOUT%%" , layout . CultureName ) ;
2019-08-30 15:59:51 +02:00
foreach ( var child in LayoutsStackPanel . Children )
{
2020-03-17 10:37:08 +01:00
if ( child is KeyboardLayoutButton layoutButton )
2019-08-30 15:59:51 +02:00
{
layoutButton . IsCurrent = layout . Id = = layoutButton . LayoutId ;
}
}
LayoutCultureCode . Text = layout . CultureCode ;
Button . ToolTip = tooltip ;
2022-10-07 14:38:31 +02:00
AutomationProperties . SetName ( Button , tooltip ) ;
2019-03-21 16:05:16 +01:00
}
2022-05-18 10:51:58 +02:00
private void Popup_KeyUp ( object sender , System . Windows . Input . KeyEventArgs e )
{
if ( e . Key = = System . Windows . Input . Key . Enter | | e . Key = = System . Windows . Input . Key . Escape )
{
Popup . IsOpen = false ;
Button . Focus ( ) ;
}
}
2019-03-21 16:05:16 +01:00
}
}