SEBWIN-110: Implemented a basic version of the keyboard layout system control.

This commit is contained in:
dbuechel 2017-09-13 08:33:12 +02:00
parent 189d06ef20
commit da7e65e7e8
23 changed files with 514 additions and 57 deletions

View file

@ -28,7 +28,7 @@ namespace SafeExamBrowser.Configuration
}
public bool AllowApplicationLog => true;
public bool AllowKeyboardLayout => true;
public string AppDataFolderName => "SafeExamBrowser";
public string ApplicationLogFile
@ -37,9 +37,7 @@ namespace SafeExamBrowser.Configuration
}
public IBrowserSettings Browser { get; private set; }
public IKeyboardSettings Keyboard { get; private set; }
public IMouseSettings Mouse { get; private set; }
public string LogFolderPath

View file

@ -15,6 +15,11 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
/// </summary>
bool AllowApplicationLog { get; }
/// <summary>
/// Determines whether the user may switch the keyboard layout during runtime.
/// </summary>
bool AllowKeyboardLayout { get; }
/// <summary>
/// The name used for the application data folder.
/// </summary>

View file

@ -41,6 +41,7 @@ namespace SafeExamBrowser.Contracts.I18n
SplashScreen_StopProcessMonitoring,
SplashScreen_StopWindowMonitoring,
SplashScreen_TerminateBrowser,
SplashScreen_TerminateTaskbar,
SplashScreen_WaitExplorerStartup,
SplashScreen_WaitExplorerTermination,
SystemControl_BatteryCharged,
@ -48,6 +49,7 @@ namespace SafeExamBrowser.Contracts.I18n
SystemControl_BatteryChargeCriticalWarning,
SystemControl_BatteryChargeLowInfo,
SystemControl_BatteryRemainingCharge,
SystemControl_KeyboardLayoutTooltip,
Version
}
}

View file

@ -87,12 +87,14 @@
<Compile Include="Monitoring\KeyState.cs" />
<Compile Include="Monitoring\MouseButton.cs" />
<Compile Include="SystemComponents\BatteryChargeStatus.cs" />
<Compile Include="SystemComponents\IKeyboardLayout.cs" />
<Compile Include="SystemComponents\ISystemComponent.cs" />
<Compile Include="UserInterface\IBrowserControl.cs" />
<Compile Include="UserInterface\IBrowserWindow.cs" />
<Compile Include="UserInterface\IMessageBox.cs" />
<Compile Include="UserInterface\Taskbar\INotificationButton.cs" />
<Compile Include="UserInterface\ISplashScreen.cs" />
<Compile Include="UserInterface\Taskbar\ISystemKeyboardLayoutControl.cs" />
<Compile Include="UserInterface\Taskbar\ISystemPowerSupplyControl.cs" />
<Compile Include="UserInterface\Taskbar\ISystemControl.cs" />
<Compile Include="UserInterface\Taskbar\ITaskbar.cs" />

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET)
*
* 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;
namespace SafeExamBrowser.Contracts.SystemComponents
{
public interface IKeyboardLayout
{
/// <summary>
/// The three-letter ISO code of the culture to which this keyboard layout is associated.
/// </summary>
string CultureCode { get; }
/// <summary>
/// The unique identifier of the keyboard layout.
/// </summary>
Guid Id { get; }
/// <summary>
/// Specifies whether this is the current keyboard layout.
/// </summary>
bool IsCurrent { get; }
/// <summary>
/// The name of this keyboard layout.
/// </summary>
string Name { get; }
}
}

View file

@ -41,6 +41,11 @@ namespace SafeExamBrowser.Contracts.UserInterface
/// </summary>
INotificationButton CreateNotification(INotificationInfo info);
/// <summary>
/// Creates a system control which allows to change the keyboard layout of the computer.
/// </summary>
ISystemKeyboardLayoutControl CreateKeyboardLayoutControl();
/// <summary>
/// Creates a system control displaying the power supply status of the computer.
/// </summary>

View file

@ -0,0 +1,27 @@
/*
* Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET)
*
* 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 SafeExamBrowser.Contracts.SystemComponents;
namespace SafeExamBrowser.Contracts.UserInterface.Taskbar
{
public delegate void KeyboardLayoutSelectedEventHandler(IKeyboardLayout layout);
public interface ISystemKeyboardLayoutControl : ISystemControl
{
/// <summary>
/// Event fired when the user selected a keyboard layout.
/// </summary>
event KeyboardLayoutSelectedEventHandler LayoutSelected;
/// <summary>
/// Adds the given layout to the list of selectable keyboard layouts.
/// </summary>
void Add(IKeyboardLayout layout);
}
}

View file

@ -21,8 +21,9 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
public class TaskbarOperation : IOperation
{
private ILogger logger;
private INotificationController aboutController, logController;
private INotificationController logController;
private ISettings settings;
private ISystemComponent<ISystemKeyboardLayoutControl> keyboardLayout;
private ISystemComponent<ISystemPowerSupplyControl> powerSupply;
private ISystemInfo systemInfo;
private ITaskbar taskbar;
@ -34,6 +35,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
public TaskbarOperation(
ILogger logger,
ISettings settings,
ISystemComponent<ISystemKeyboardLayoutControl> keyboardLayout,
ISystemComponent<ISystemPowerSupplyControl> powerSupply,
ISystemInfo systemInfo,
ITaskbar taskbar,
@ -42,6 +44,7 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
{
this.logger = logger;
this.settings = settings;
this.keyboardLayout = keyboardLayout;
this.powerSupply = powerSupply;
this.systemInfo = systemInfo;
this.taskbar = taskbar;
@ -59,16 +62,31 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
CreateLogNotification();
}
if (settings.AllowKeyboardLayout)
{
AddKeyboardLayoutControl();
}
if (systemInfo.HasBattery)
{
CreatePowerSupplyComponent();
AddPowerSupplyControl();
}
}
public void Revert()
{
logController?.Terminate();
aboutController?.Terminate();
logger.Info("Terminating taskbar...");
SplashScreen.UpdateText(TextKey.SplashScreen_TerminateTaskbar);
if (settings.AllowApplicationLog)
{
logController?.Terminate();
}
if (settings.AllowKeyboardLayout)
{
keyboardLayout.Terminate();
}
if (systemInfo.HasBattery)
{
@ -76,6 +94,22 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
}
}
private void AddKeyboardLayoutControl()
{
var control = uiFactory.CreateKeyboardLayoutControl();
keyboardLayout.Initialize(control);
taskbar.AddSystemControl(control);
}
private void AddPowerSupplyControl()
{
var control = uiFactory.CreatePowerSupplyControl();
powerSupply.Initialize(control);
taskbar.AddSystemControl(control);
}
private void CreateLogNotification()
{
var logInfo = new LogNotificationInfo(text);
@ -86,13 +120,5 @@ namespace SafeExamBrowser.Core.Behaviour.Operations
taskbar.AddNotification(logNotification);
}
private void CreatePowerSupplyComponent()
{
var control = uiFactory.CreatePowerSupplyControl();
powerSupply.Initialize(control);
taskbar.AddSystemControl(control);
}
}
}

View file

@ -1,37 +1,39 @@
<?xml version="1.0" encoding="utf-8" ?>
<Text>
<Browser_ShowDeveloperConsole>Open Console</Browser_ShowDeveloperConsole>
<LogWindow_Title>Application Log</LogWindow_Title>
<MessageBox_ShutdownError>An unexpected error occurred during the shutdown procedure! Please consult the application log for more information...</MessageBox_ShutdownError>
<MessageBox_ShutdownErrorTitle>Shutdown Error</MessageBox_ShutdownErrorTitle>
<MessageBox_StartupError>An unexpected error occurred during the startup procedure! Please consult the application log for more information...</MessageBox_StartupError>
<MessageBox_StartupErrorTitle>Startup Error</MessageBox_StartupErrorTitle>
<Notification_AboutTooltip>About Safe Exam Browser</Notification_AboutTooltip>
<Notification_LogTooltip>Application Log</Notification_LogTooltip>
<SplashScreen_EmptyClipboard>Emptying clipboard</SplashScreen_EmptyClipboard>
<SplashScreen_InitializeBrowser>Initializing browser</SplashScreen_InitializeBrowser>
<SplashScreen_InitializeProcessMonitoring>Initializing process monitoring</SplashScreen_InitializeProcessMonitoring>
<SplashScreen_InitializeTaskbar>Initializing taskbar</SplashScreen_InitializeTaskbar>
<SplashScreen_InitializeWindowMonitoring>Initializing window monitoring</SplashScreen_InitializeWindowMonitoring>
<SplashScreen_InitializeWorkingArea>Initializing working area</SplashScreen_InitializeWorkingArea>
<SplashScreen_RestoreWorkingArea>Restoring working area</SplashScreen_RestoreWorkingArea>
<SplashScreen_ShutdownProcedure>Initiating shutdown procedure</SplashScreen_ShutdownProcedure>
<SplashScreen_StartEventHandling>Starting event handling</SplashScreen_StartEventHandling>
<SplashScreen_StartKeyboardInterception>Starting keyboard interception</SplashScreen_StartKeyboardInterception>
<SplashScreen_StartMouseInterception>Starting mouse interception</SplashScreen_StartMouseInterception>
<SplashScreen_StartupProcedure>Initiating startup procedure</SplashScreen_StartupProcedure>
<SplashScreen_StopEventHandling>Stopping event handling</SplashScreen_StopEventHandling>
<SplashScreen_StopKeyboardInterception>Stopping keyboard interception</SplashScreen_StopKeyboardInterception>
<SplashScreen_StopMouseInterception>Stopping mouse interception</SplashScreen_StopMouseInterception>
<SplashScreen_StopProcessMonitoring>Stopping process monitoring</SplashScreen_StopProcessMonitoring>
<SplashScreen_StopWindowMonitoring>Stopping window monitoring</SplashScreen_StopWindowMonitoring>
<SplashScreen_TerminateBrowser>Terminating browser</SplashScreen_TerminateBrowser>
<SplashScreen_WaitExplorerStartup>Waiting for Windows explorer to start up</SplashScreen_WaitExplorerStartup>
<SplashScreen_WaitExplorerTermination>Waiting for Windows explorer to shut down</SplashScreen_WaitExplorerTermination>
<SystemControl_BatteryCharging>Plugged in, charging... (%%CHARGE%%%)</SystemControl_BatteryCharging>
<SystemControl_BatteryCharged>Fully charged (%%CHARGE%%%)</SystemControl_BatteryCharged>
<SystemControl_BatteryChargeCriticalWarning>The battery charge is critically low. Please connect your computer to a power supply!</SystemControl_BatteryChargeCriticalWarning>
<SystemControl_BatteryChargeLowInfo>The battery charge is getting low. Consider connecting your computer to a power supply in time...</SystemControl_BatteryChargeLowInfo>
<SystemControl_BatteryRemainingCharge>%%HOURS%%h %%MINUTES%%min remaining (%%CHARGE%%%)</SystemControl_BatteryRemainingCharge>
<Version>Version</Version>
<Browser_ShowDeveloperConsole>Open Console</Browser_ShowDeveloperConsole>
<LogWindow_Title>Application Log</LogWindow_Title>
<MessageBox_ShutdownError>An unexpected error occurred during the shutdown procedure! Please consult the application log for more information...</MessageBox_ShutdownError>
<MessageBox_ShutdownErrorTitle>Shutdown Error</MessageBox_ShutdownErrorTitle>
<MessageBox_StartupError>An unexpected error occurred during the startup procedure! Please consult the application log for more information...</MessageBox_StartupError>
<MessageBox_StartupErrorTitle>Startup Error</MessageBox_StartupErrorTitle>
<Notification_AboutTooltip>About Safe Exam Browser</Notification_AboutTooltip>
<Notification_LogTooltip>Application Log</Notification_LogTooltip>
<SplashScreen_EmptyClipboard>Emptying clipboard</SplashScreen_EmptyClipboard>
<SplashScreen_InitializeBrowser>Initializing browser</SplashScreen_InitializeBrowser>
<SplashScreen_InitializeProcessMonitoring>Initializing process monitoring</SplashScreen_InitializeProcessMonitoring>
<SplashScreen_InitializeTaskbar>Initializing taskbar</SplashScreen_InitializeTaskbar>
<SplashScreen_InitializeWindowMonitoring>Initializing window monitoring</SplashScreen_InitializeWindowMonitoring>
<SplashScreen_InitializeWorkingArea>Initializing working area</SplashScreen_InitializeWorkingArea>
<SplashScreen_RestoreWorkingArea>Restoring working area</SplashScreen_RestoreWorkingArea>
<SplashScreen_ShutdownProcedure>Initiating shutdown procedure</SplashScreen_ShutdownProcedure>
<SplashScreen_StartEventHandling>Starting event handling</SplashScreen_StartEventHandling>
<SplashScreen_StartKeyboardInterception>Starting keyboard interception</SplashScreen_StartKeyboardInterception>
<SplashScreen_StartMouseInterception>Starting mouse interception</SplashScreen_StartMouseInterception>
<SplashScreen_StartupProcedure>Initiating startup procedure</SplashScreen_StartupProcedure>
<SplashScreen_StopEventHandling>Stopping event handling</SplashScreen_StopEventHandling>
<SplashScreen_StopKeyboardInterception>Stopping keyboard interception</SplashScreen_StopKeyboardInterception>
<SplashScreen_StopMouseInterception>Stopping mouse interception</SplashScreen_StopMouseInterception>
<SplashScreen_StopProcessMonitoring>Stopping process monitoring</SplashScreen_StopProcessMonitoring>
<SplashScreen_StopWindowMonitoring>Stopping window monitoring</SplashScreen_StopWindowMonitoring>
<SplashScreen_TerminateBrowser>Terminating browser</SplashScreen_TerminateBrowser>
<SplashScreen_TerminateTaskbar>Terminating taskbar</SplashScreen_TerminateTaskbar>
<SplashScreen_WaitExplorerStartup>Waiting for Windows explorer to start up</SplashScreen_WaitExplorerStartup>
<SplashScreen_WaitExplorerTermination>Waiting for Windows explorer to shut down</SplashScreen_WaitExplorerTermination>
<SystemControl_BatteryCharging>Plugged in, charging... (%%CHARGE%%%)</SystemControl_BatteryCharging>
<SystemControl_BatteryCharged>Fully charged (%%CHARGE%%%)</SystemControl_BatteryCharged>
<SystemControl_BatteryChargeCriticalWarning>The battery charge is critically low. Please connect your computer to a power supply!</SystemControl_BatteryChargeCriticalWarning>
<SystemControl_BatteryChargeLowInfo>The battery charge is getting low. Consider connecting your computer to a power supply in time...</SystemControl_BatteryChargeLowInfo>
<SystemControl_BatteryRemainingCharge>%%HOURS%%h %%MINUTES%%min remaining (%%CHARGE%%%)</SystemControl_BatteryRemainingCharge>
<SystemControl_KeyboardLayoutTooltip>Click to choose a different keyboard layout...</SystemControl_KeyboardLayoutTooltip>
<Version>Version</Version>
</Text>

View file

@ -0,0 +1,90 @@
/*
* Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET)
*
* 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.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
namespace SafeExamBrowser.SystemComponents
{
public class KeyboardLayout : ISystemComponent<ISystemKeyboardLayoutControl>
{
private IList<KeyboardLayoutDefinition> layouts = new List<KeyboardLayoutDefinition>();
private ILogger logger;
private InputLanguage originalLanguage;
private ISystemKeyboardLayoutControl control;
private IText text;
public KeyboardLayout(ILogger logger, IText text)
{
this.logger = logger;
this.text = text;
}
public void Initialize(ISystemKeyboardLayoutControl control)
{
this.control = control;
originalLanguage = InputLanguage.CurrentInputLanguage;
control.LayoutSelected += Control_LayoutSelected;
control.SetTooltip(text.Get(TextKey.SystemControl_KeyboardLayoutTooltip));
logger.Info($"Saved current keyboard layout {ToString(originalLanguage)}.");
foreach (InputLanguage language in InputLanguage.InstalledInputLanguages)
{
var layout = new KeyboardLayoutDefinition
{
CultureCode = language.Culture.ThreeLetterISOLanguageName.ToUpper(),
IsCurrent = originalLanguage.Equals(language),
Language = language,
Name = language.LayoutName
};
control.Add(layout);
layouts.Add(layout);
logger.Info($"Added keyboard layout {ToString(language)} to system control.");
}
}
public void Terminate()
{
control?.Close();
if (originalLanguage != null)
{
InputLanguage.CurrentInputLanguage = originalLanguage;
logger.Info($"Restored original keyboard layout {ToString(originalLanguage)}.");
}
}
private void Control_LayoutSelected(IKeyboardLayout layout)
{
var language = layouts.First(l => l.Id == layout.Id).Language;
InputLanguage.CurrentInputLanguage = language;
foreach (var l in layouts)
{
l.IsCurrent = l.Id == layout.Id;
}
logger.Info($"Changed keyboard layout to {ToString(language)}.");
}
private string ToString(InputLanguage language)
{
return $"'{language.LayoutName}' ({language.Culture.ThreeLetterISOLanguageName.ToUpper()})";
}
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET)
*
* 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.Windows.Forms;
using SafeExamBrowser.Contracts.SystemComponents;
namespace SafeExamBrowser.SystemComponents
{
internal class KeyboardLayoutDefinition : IKeyboardLayout
{
internal InputLanguage Language { get; set; }
public string CultureCode { get; set; }
public Guid Id { get; }
public bool IsCurrent { get; set; }
public string Name { get; set; }
public KeyboardLayoutDefinition()
{
Id = Guid.NewGuid();
}
}
}

View file

@ -47,11 +47,6 @@ namespace SafeExamBrowser.SystemComponents
logger.Info("Started monitoring the power supply.");
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
UpdateControl();
}
public void Terminate()
{
timer?.Stop();
@ -59,6 +54,11 @@ namespace SafeExamBrowser.SystemComponents
logger.Info("Stopped monitoring the power supply.");
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
UpdateControl();
}
private void UpdateControl()
{
var charge = SystemInformation.PowerStatus.BatteryLifePercent;

View file

@ -53,6 +53,8 @@
<Reference Include="System.Windows.Forms" />
</ItemGroup>
<ItemGroup>
<Compile Include="KeyboardLayoutDefinition.cs" />
<Compile Include="KeyboardLayout.cs" />
<Compile Include="PowerSupply.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>

View file

@ -53,7 +53,7 @@ namespace SafeExamBrowser.UserInterface.Classic.Controls
Button.MouseEnter += (o, args) => InstancePopup.IsOpen = instances.Count > 1;
Button.MouseLeave += (o, args) => InstancePopup.IsOpen = InstancePopup.IsMouseOver;
InstancePopup.MouseLeave += (o, args) => InstancePopup.IsOpen = false || IsMouseOver;
InstancePopup.MouseLeave += (o, args) => InstancePopup.IsOpen = IsMouseOver;
InstancePopup.Opened += (o, args) =>
{

View file

@ -0,0 +1,30 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Classic.Controls.KeyboardLayoutButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Classic.Controls"
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="250">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Templates/Buttons.xaml" />
<ResourceDictionary Source="../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Button x:Name="Button" Height="40" Padding="10,0" Template="{StaticResource TaskbarButton}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="IsCurrentTextBlock" Grid.Column="0" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Hidden">•</TextBlock>
<TextBlock x:Name="CultureCodeTextBlock" Grid.Column="1" FontWeight="Bold" HorizontalAlignment="Left" Margin="10,0,5,0" VerticalAlignment="Center" />
<TextBlock x:Name="LayoutNameTextBlock" Grid.Column="2" Foreground="Gray" HorizontalAlignment="Left" Margin="5,0,10,0" VerticalAlignment="Center" />
</Grid>
</Button>
</Grid>
</UserControl>

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET)
*
* 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.Windows;
using System.Windows.Controls;
namespace SafeExamBrowser.UserInterface.Classic.Controls
{
public partial class KeyboardLayoutButton : UserControl
{
public event RoutedEventHandler Click;
public string CultureCode
{
set { CultureCodeTextBlock.Text = value; }
}
public bool IsCurrent
{
set { IsCurrentTextBlock.Visibility = value ? Visibility.Visible : Visibility.Hidden; }
}
public string LayoutName
{
set { LayoutNameTextBlock.Text = value; }
}
public KeyboardLayoutButton()
{
InitializeComponent();
Button.Click += (o, args) => Click?.Invoke(o, args);
}
}
}

View file

@ -0,0 +1,40 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Classic.Controls.KeyboardLayoutControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Classic.Controls"
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="40">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Templates/Buttons.xaml" />
<ResourceDictionary Source="../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Popup x:Name="Popup" IsOpen="False" Placement="Top" PlacementTarget="{Binding ElementName=Button}">
<Border Background="LightGray" BorderBrush="Gray" BorderThickness="0.5,0.5,0.5,0">
<ScrollViewer x:Name="LayoutsScrollViewer" Background="{StaticResource BackgroundBrush}" MaxHeight="250" VerticalScrollBarVisibility="Auto">
<ScrollViewer.Resources>
<s:Double x:Key="{x:Static SystemParameters.VerticalScrollBarWidthKey}">5</s:Double>
</ScrollViewer.Resources>
<StackPanel x:Name="LayoutsStackPanel" />
</ScrollViewer>
</Border>
</Popup>
<Button x:Name="Button" Background="Transparent" HorizontalContentAlignment="Stretch" Template="{StaticResource TaskbarButton}"
VerticalContentAlignment="Stretch" Width="40">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock x:Name="LayoutCultureCode" Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Bottom" />
<TextBlock x:Name="LayoutName" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Top" />
</Grid>
</Button>
</Grid>
</UserControl>

View file

@ -0,0 +1,97 @@
/*
* Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET)
*
* 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.Windows.Controls;
using System.Windows.Media;
using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
namespace SafeExamBrowser.UserInterface.Classic.Controls
{
public partial class KeyboardLayoutControl : UserControl, ISystemKeyboardLayoutControl
{
public event KeyboardLayoutSelectedEventHandler LayoutSelected;
public KeyboardLayoutControl()
{
InitializeComponent();
InitializeKeyboardLayoutControl();
}
public void Add(IKeyboardLayout layout)
{
var button = new KeyboardLayoutButton();
button.Click += (o, args) =>
{
SetCurrent(button, layout);
Popup.IsOpen = false;
LayoutSelected?.Invoke(layout);
};
button.CultureCode = layout.CultureCode;
button.LayoutName = layout.Name;
LayoutsStackPanel.Children.Add(button);
if (layout.IsCurrent)
{
SetCurrent(button, layout);
}
}
public void Close()
{
Popup.IsOpen = false;
}
public void SetTooltip(string text)
{
Button.ToolTip = text;
}
private void InitializeKeyboardLayoutControl()
{
var originalBrush = Button.Background;
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
Button.MouseLeave += (o, args) => Popup.IsOpen = Popup.IsMouseOver;
Popup.MouseLeave += (o, args) => Popup.IsOpen = IsMouseOver;
Popup.Opened += (o, args) =>
{
Background = Brushes.LightBlue;
Button.Background = Brushes.LightBlue;
};
Popup.Closed += (o, args) =>
{
Background = originalBrush;
Button.Background = originalBrush;
};
}
private void SetCurrent(KeyboardLayoutButton button, IKeyboardLayout layout)
{
var name = layout.Name?.Length > 3 ? String.Join(string.Empty, layout.Name.Split(' ').Select(s => s.First())) : layout.Name;
foreach (var child in LayoutsStackPanel.Children)
{
if (child is KeyboardLayoutButton)
{
(child as KeyboardLayoutButton).IsCurrent = false;
}
}
button.IsCurrent = true;
LayoutCultureCode.Text = layout.CultureCode;
LayoutName.Text = name;
}
}
}

View file

@ -1,4 +1,4 @@
<Viewbox Width="1024.000" Height="1024.000"
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="1024.000" Height="1024.000">

View file

@ -78,6 +78,12 @@
<Compile Include="Controls\DateTimeControl.xaml.cs">
<DependentUpon>DateTimeControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\KeyboardLayoutButton.xaml.cs">
<DependentUpon>KeyboardLayoutButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\KeyboardLayoutControl.xaml.cs">
<DependentUpon>KeyboardLayoutControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\NotificationButton.xaml.cs">
<DependentUpon>NotificationButton.xaml</DependentUpon>
</Compile>
@ -120,6 +126,14 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\KeyboardLayoutButton.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\KeyboardLayoutControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\NotificationButton.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>

View file

@ -65,6 +65,11 @@ namespace SafeExamBrowser.UserInterface.Classic
return new NotificationButton(info);
}
public ISystemKeyboardLayoutControl CreateKeyboardLayoutControl()
{
return new KeyboardLayoutControl();
}
public ISystemPowerSupplyControl CreatePowerSupplyControl()
{
return new PowerSupplyControl();

View file

@ -65,6 +65,12 @@ namespace SafeExamBrowser.UserInterface.Windows10
return new NotificationButton(info);
}
public ISystemKeyboardLayoutControl CreateKeyboardLayoutControl()
{
// TODO
throw new System.NotImplementedException();
}
public ISystemPowerSupplyControl CreatePowerSupplyControl()
{
return new PowerSupplyControl();

View file

@ -47,6 +47,7 @@ namespace SafeExamBrowser
private IProcessMonitor processMonitor;
private IRuntimeController runtimeController;
private ISettings settings;
private ISystemComponent<ISystemKeyboardLayoutControl> keyboardLayout;
private ISystemComponent<ISystemPowerSupplyControl> powerSupply;
private ISystemInfo systemInfo;
private IText text;
@ -77,6 +78,7 @@ namespace SafeExamBrowser
browserController = new BrowserApplicationController(settings, text, uiFactory);
displayMonitor = new DisplayMonitor(new ModuleLogger(logger, typeof(DisplayMonitor)), nativeMethods);
keyboardInterceptor = new KeyboardInterceptor(settings.Keyboard, new ModuleLogger(logger, typeof(KeyboardInterceptor)));
keyboardLayout = new KeyboardLayout(new ModuleLogger(logger, typeof(KeyboardLayout)), text);
mouseInterceptor = new MouseInterceptor(new ModuleLogger(logger, typeof(MouseInterceptor)), settings.Mouse);
powerSupply = new PowerSupply(new ModuleLogger(logger, typeof(PowerSupply)), text);
processMonitor = new ProcessMonitor(new ModuleLogger(logger, typeof(ProcessMonitor)), nativeMethods);
@ -91,7 +93,7 @@ namespace SafeExamBrowser
StartupOperations.Enqueue(new WindowMonitorOperation(logger, windowMonitor));
StartupOperations.Enqueue(new ProcessMonitorOperation(logger, processMonitor));
StartupOperations.Enqueue(new DisplayMonitorOperation(displayMonitor, logger, Taskbar));
StartupOperations.Enqueue(new TaskbarOperation(logger, settings, powerSupply, systemInfo, Taskbar, text, uiFactory));
StartupOperations.Enqueue(new TaskbarOperation(logger, settings, keyboardLayout, powerSupply, systemInfo, Taskbar, text, uiFactory));
StartupOperations.Enqueue(new BrowserOperation(browserController, browserInfo, logger, Taskbar, uiFactory));
StartupOperations.Enqueue(new RuntimeControllerOperation(runtimeController, logger));
StartupOperations.Enqueue(new ClipboardOperation(logger, nativeMethods));