SEBWIN-141: Extended / changed implementation of keyboard layout for action center and taskbar.

This commit is contained in:
dbuechel 2019-03-12 16:18:27 +01:00
parent 9e0a3d8543
commit 5ba6e6345c
27 changed files with 412 additions and 138 deletions

View file

@ -59,8 +59,8 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
taskbarSettings = new TaskbarSettings(); taskbarSettings = new TaskbarSettings();
uiFactoryMock = new Mock<IUserInterfaceFactory>(); uiFactoryMock = new Mock<IUserInterfaceFactory>();
taskbarSettings.AllowApplicationLog = true; taskbarSettings.ShowApplicationLog = true;
taskbarSettings.AllowKeyboardLayout = true; taskbarSettings.ShowKeyboardLayout = true;
taskbarSettings.AllowWirelessNetwork = true; taskbarSettings.AllowWirelessNetwork = true;
taskbarSettings.EnableTaskbar = true; taskbarSettings.EnableTaskbar = true;
systemInfoMock.SetupGet(s => s.HasBattery).Returns(true); systemInfoMock.SetupGet(s => s.HasBattery).Returns(true);

View file

@ -191,7 +191,7 @@ namespace SafeExamBrowser.Client.Operations
private void InitializeLogNotificationForActionCenter() private void InitializeLogNotificationForActionCenter()
{ {
if (actionCenterSettings.AllowApplicationLog) if (actionCenterSettings.ShowApplicationLog)
{ {
var notification = uiFactory.CreateNotificationControl(logInfo, Location.ActionCenter); var notification = uiFactory.CreateNotificationControl(logInfo, Location.ActionCenter);
@ -202,7 +202,7 @@ namespace SafeExamBrowser.Client.Operations
private void InitializeLogNotificationForTaskbar() private void InitializeLogNotificationForTaskbar()
{ {
if (taskbarSettings.AllowApplicationLog) if (taskbarSettings.ShowApplicationLog)
{ {
var notification = uiFactory.CreateNotificationControl(logInfo, Location.Taskbar); var notification = uiFactory.CreateNotificationControl(logInfo, Location.Taskbar);
@ -213,14 +213,20 @@ namespace SafeExamBrowser.Client.Operations
private void InitializeKeyboardLayoutForActionCenter() private void InitializeKeyboardLayoutForActionCenter()
{ {
// TODO if (actionCenterSettings.ShowKeyboardLayout)
{
var control = uiFactory.CreateKeyboardLayoutControl(Location.ActionCenter);
keyboardLayout.Register(control);
actionCenter.AddSystemControl(control);
}
} }
private void InitializeKeyboardLayoutForTaskbar() private void InitializeKeyboardLayoutForTaskbar()
{ {
if (taskbarSettings.AllowKeyboardLayout) if (taskbarSettings.ShowKeyboardLayout)
{ {
var control = uiFactory.CreateKeyboardLayoutControl(); var control = uiFactory.CreateKeyboardLayoutControl(Location.Taskbar);
keyboardLayout.Register(control); keyboardLayout.Register(control);
taskbar.AddSystemControl(control); taskbar.AddSystemControl(control);

View file

@ -16,7 +16,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{ {
if (value is bool allow) if (value is bool allow)
{ {
settings.Taskbar.AllowApplicationLog = allow; settings.Taskbar.ShowApplicationLog = allow;
} }
} }
@ -32,7 +32,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{ {
if (value is bool enabled) if (value is bool enabled)
{ {
settings.Taskbar.AllowKeyboardLayout = enabled; settings.Taskbar.ShowKeyboardLayout = enabled;
} }
} }

View file

@ -138,17 +138,18 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
settings.ServicePolicy = ServicePolicy.Optional; settings.ServicePolicy = ServicePolicy.Optional;
settings.Taskbar.AllowApplicationLog = false; settings.Taskbar.ShowApplicationLog = false;
settings.Taskbar.AllowKeyboardLayout = true; settings.Taskbar.ShowKeyboardLayout = true;
settings.Taskbar.AllowWirelessNetwork = false; settings.Taskbar.AllowWirelessNetwork = false;
settings.Taskbar.EnableTaskbar = true; settings.Taskbar.EnableTaskbar = true;
settings.Taskbar.ShowClock = true; settings.Taskbar.ShowClock = true;
// TODO: Default values for testing of alpha version only, remove for final release! // TODO: Default values for testing of alpha version only, remove for final release!
settings.ActionCenter.AllowApplicationLog = true; settings.ActionCenter.ShowApplicationLog = true;
settings.ActionCenter.ShowKeyboardLayout = true;
settings.Browser.AllowDeveloperConsole = true; settings.Browser.AllowDeveloperConsole = true;
settings.Browser.MainWindowSettings.AllowAddressBar = true; settings.Browser.MainWindowSettings.AllowAddressBar = true;
settings.Taskbar.AllowApplicationLog = true; settings.Taskbar.ShowApplicationLog = true;
return settings; return settings;
} }

View file

@ -16,14 +16,19 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
[Serializable] [Serializable]
public class ActionCenterSettings public class ActionCenterSettings
{ {
/// <summary>
/// Determines whether the user may access the application log during runtime.
/// </summary>
public bool AllowApplicationLog { get; set; }
/// <summary> /// <summary>
/// Determines whether the action center itself is enabled and visible to the user. /// Determines whether the action center itself is enabled and visible to the user.
/// </summary> /// </summary>
public bool EnableActionCenter { get; set; } public bool EnableActionCenter { get; set; }
/// <summary>
/// Determines whether the application log is accessible via the action center.
/// </summary>
public bool ShowApplicationLog { get; set; }
/// <summary>
/// Determines whether the system control for the keyboard layout is accessible via the action center.
/// </summary>
public bool ShowKeyboardLayout { get; set; }
} }
} }

View file

@ -16,29 +16,29 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
[Serializable] [Serializable]
public class TaskbarSettings public class TaskbarSettings
{ {
/// <summary>
/// Determines whether the user may switch the keyboard layout during runtime.
/// </summary>
public bool AllowKeyboardLayout { get; set; }
/// <summary>
/// Determines whether the user may access the application log during runtime.
/// </summary>
public bool AllowApplicationLog { get; set; }
/// <summary>
/// Determines whether the user may control the wireless network connection during runtime.
/// </summary>
public bool AllowWirelessNetwork { get; set; }
/// <summary> /// <summary>
/// Determines whether the taskbar itself is enabled and visible to the user. /// Determines whether the taskbar itself is enabled and visible to the user.
/// </summary> /// </summary>
public bool EnableTaskbar { get; set; } public bool EnableTaskbar { get; set; }
/// <summary>
/// Determines whether the application log is accessible via the taskbar.
/// </summary>
public bool ShowApplicationLog { get; set; }
/// <summary> /// <summary>
/// Determines whether the current date and time will be rendered in the taskbar. /// Determines whether the current date and time will be rendered in the taskbar.
/// </summary> /// </summary>
public bool ShowClock { get; set; } public bool ShowClock { get; set; }
/// <summary>
/// Determines whether the system control for the keyboard layout is accessible via the taskbar.
/// </summary>
public bool ShowKeyboardLayout { get; set; }
/// <summary>
/// Determines whether the system control for the wireless network is accessible via the taskbar.
/// </summary>
public bool AllowWirelessNetwork { get; set; }
} }
} }

View file

@ -25,11 +25,6 @@ namespace SafeExamBrowser.Contracts.SystemComponents
/// </summary> /// </summary>
Guid Id { get; } Guid Id { get; }
/// <summary>
/// Specifies whether this is the current keyboard layout.
/// </summary>
bool IsCurrent { get; }
/// <summary> /// <summary>
/// The name of this keyboard layout. /// The name of this keyboard layout.
/// </summary> /// </summary>

View file

@ -43,7 +43,7 @@ namespace SafeExamBrowser.Contracts.UserInterface
/// <summary> /// <summary>
/// Creates a system control which allows to change the keyboard layout of the computer. /// Creates a system control which allows to change the keyboard layout of the computer.
/// </summary> /// </summary>
ISystemKeyboardLayoutControl CreateKeyboardLayoutControl(); ISystemKeyboardLayoutControl CreateKeyboardLayoutControl(Location location);
/// <summary> /// <summary>
/// Creates a new log window which runs on its own thread. /// Creates a new log window which runs on its own thread.

View file

@ -6,12 +6,12 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
using SafeExamBrowser.Contracts.SystemComponents; using System;
namespace SafeExamBrowser.Contracts.UserInterface.Shell.Events namespace SafeExamBrowser.Contracts.UserInterface.Shell.Events
{ {
/// <summary> /// <summary>
/// Indicates that a particular <see cref="IKeyboardLayout"/> has been selected by the user. /// Indicates that a keyboard layout has been selected by the user.
/// </summary> /// </summary>
public delegate void KeyboardLayoutSelectedEventHandler(IKeyboardLayout layout); public delegate void KeyboardLayoutSelectedEventHandler(Guid id);
} }

View file

@ -25,5 +25,10 @@ namespace SafeExamBrowser.Contracts.UserInterface.Shell
/// Adds the given layout to the list of selectable keyboard layouts. /// Adds the given layout to the list of selectable keyboard layouts.
/// </summary> /// </summary>
void Add(IKeyboardLayout layout); void Add(IKeyboardLayout layout);
/// <summary>
/// Defines the given keyboard layout as the currently active one.
/// </summary>
void SetCurrent(IKeyboardLayout layout);
} }
} }

View file

@ -6,9 +6,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Windows.Forms; using System.Windows.Input;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.SystemComponents; using SafeExamBrowser.Contracts.SystemComponents;
@ -18,57 +20,70 @@ namespace SafeExamBrowser.SystemComponents
{ {
public class KeyboardLayout : ISystemComponent<ISystemKeyboardLayoutControl> public class KeyboardLayout : ISystemComponent<ISystemKeyboardLayoutControl>
{ {
private IList<KeyboardLayoutDefinition> layouts = new List<KeyboardLayoutDefinition>(); private const int TWO_SECONDS = 2000;
private KeyboardLayoutDefinition currentLayout;
private IList<KeyboardLayoutDefinition> layouts;
private ILogger logger; private ILogger logger;
private InputLanguage originalLanguage; private CultureInfo originalLanguage;
private IList<ISystemKeyboardLayoutControl> controls; private IList<ISystemKeyboardLayoutControl> controls;
private IText text; private IText text;
public KeyboardLayout(ILogger logger, IText text) public KeyboardLayout(ILogger logger, IText text)
{ {
this.controls = new List<ISystemKeyboardLayoutControl>(); this.controls = new List<ISystemKeyboardLayoutControl>();
this.layouts = new List<KeyboardLayoutDefinition>();
this.logger = logger; this.logger = logger;
this.text = text; this.text = text;
} }
public void Initialize() public void Initialize()
{ {
originalLanguage = InputLanguage.CurrentInputLanguage; originalLanguage = InputLanguageManager.Current.CurrentInputLanguage;
logger.Info($"Saved current keyboard layout {ToString(originalLanguage)}."); logger.Info($"Saved current keyboard layout {ToString(originalLanguage)}.");
foreach (InputLanguage language in InputLanguage.InstalledInputLanguages) foreach (CultureInfo info in InputLanguageManager.Current.AvailableInputLanguages)
{ {
var layout = new KeyboardLayoutDefinition var layout = new KeyboardLayoutDefinition
{ {
CultureCode = language.Culture.ThreeLetterISOLanguageName.ToUpper(), CultureCode = info.ThreeLetterISOLanguageName.ToUpper(),
IsCurrent = originalLanguage.Equals(language), CultureInfo = info,
Language = language, Name = info.NativeName
Name = language.LayoutName
}; };
if (originalLanguage.Equals(info))
{
currentLayout = layout;
}
layouts.Add(layout); layouts.Add(layout);
logger.Info($"Detected keyboard layout {ToString(language)}."); logger.Info($"Detected keyboard layout {ToString(info)}.");
} }
InputLanguageManager.Current.InputLanguageChanged += Current_InputLanguageChanged;
} }
public void Register(ISystemKeyboardLayoutControl control) public void Register(ISystemKeyboardLayoutControl control)
{ {
control.LayoutSelected += Control_LayoutSelected;
control.SetTooltip(text.Get(TextKey.SystemControl_KeyboardLayoutTooltip));
foreach (var layout in layouts) foreach (var layout in layouts)
{ {
control.Add(layout); control.Add(layout);
} }
control.LayoutSelected += Control_LayoutSelected;
control.SetCurrent(currentLayout);
control.SetTooltip(text.Get(TextKey.SystemControl_KeyboardLayoutTooltip));
controls.Add(control); controls.Add(control);
} }
public void Terminate() public void Terminate()
{ {
InputLanguageManager.Current.InputLanguageChanged -= Current_InputLanguageChanged;
if (originalLanguage != null) if (originalLanguage != null)
{ {
InputLanguage.CurrentInputLanguage = originalLanguage; InputLanguageManager.Current.CurrentInputLanguage = originalLanguage;
logger.Info($"Restored original keyboard layout {ToString(originalLanguage)}."); logger.Info($"Restored original keyboard layout {ToString(originalLanguage)}.");
} }
@ -78,23 +93,30 @@ namespace SafeExamBrowser.SystemComponents
} }
} }
private void Control_LayoutSelected(IKeyboardLayout layout) private void Control_LayoutSelected(Guid id)
{ {
var language = layouts.First(l => l.Id == layout.Id).Language; var layout = layouts.First(l => l.Id == id);
InputLanguage.CurrentInputLanguage = language; InputLanguageManager.Current.CurrentInputLanguage = layout.CultureInfo;
logger.Info($"Changed keyboard layout to {ToString(layout.CultureInfo)}.");
foreach (var l in layouts)
{
l.IsCurrent = l.Id == layout.Id;
}
logger.Info($"Changed keyboard layout to {ToString(language)}.");
} }
private string ToString(InputLanguage language) private void Current_InputLanguageChanged(object sender, InputLanguageEventArgs e)
{ {
return $"'{language.LayoutName}' ({language.Culture.ThreeLetterISOLanguageName.ToUpper()})"; var newLayout = layouts.First(l => l.CultureInfo.Equals(e.NewLanguage));
logger.Info($"Detected keyboard layout change from {ToString(e.PreviousLanguage)} to {ToString(e.NewLanguage)}.");
currentLayout = newLayout;
foreach (var control in controls)
{
control.SetCurrent(newLayout);
}
}
private string ToString(CultureInfo info)
{
return $"'{info.DisplayName}' ({info.ThreeLetterISOLanguageName.ToUpper()})";
} }
} }
} }

View file

@ -7,18 +7,17 @@
*/ */
using System; using System;
using System.Windows.Forms; using System.Globalization;
using SafeExamBrowser.Contracts.SystemComponents; using SafeExamBrowser.Contracts.SystemComponents;
namespace SafeExamBrowser.SystemComponents namespace SafeExamBrowser.SystemComponents
{ {
internal class KeyboardLayoutDefinition : IKeyboardLayout internal class KeyboardLayoutDefinition : IKeyboardLayout
{ {
internal InputLanguage Language { get; set; } internal CultureInfo CultureInfo { get; set; }
public string CultureCode { get; set; } public string CultureCode { get; set; }
public Guid Id { get; } public Guid Id { get; }
public bool IsCurrent { get; set; }
public string Name { get; set; } public string Name { get; set; }
public KeyboardLayoutDefinition() public KeyboardLayoutDefinition()

View file

@ -47,6 +47,7 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="PresentationCore" />
<Reference Include="SimpleWifi, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="SimpleWifi, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\Libraries\SimpleWifi.dll</HintPath> <HintPath>..\Libraries\SimpleWifi.dll</HintPath>

View file

@ -20,6 +20,6 @@
<ScrollViewer Grid.Row="0" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" Template="{StaticResource SmallBarScrollViewer}"> <ScrollViewer Grid.Row="0" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" Template="{StaticResource SmallBarScrollViewer}">
<StackPanel x:Name="ApplicationPanel" Orientation="Vertical" /> <StackPanel x:Name="ApplicationPanel" Orientation="Vertical" />
</ScrollViewer> </ScrollViewer>
<UniformGrid x:Name="ControlPanel" Grid.Row="1" Columns="4" Margin="5" /> <UniformGrid x:Name="ControlPanel" Grid.Row="1" Columns="4" Margin="10" />
</Grid> </Grid>
</Window> </Window>

View file

@ -0,0 +1,30 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenterKeyboardLayoutButton"
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.Desktop.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" Background="Transparent" Height="40" Padding="10,0" Template="{StaticResource ActionCenterButton}">
<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="White" HorizontalAlignment="Left" Margin="5,0,10,0" VerticalAlignment="Center" />
</Grid>
</Button>
</Grid>
</UserControl>

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2019 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;
using System.Windows.Controls;
using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
namespace SafeExamBrowser.UserInterface.Desktop.Controls
{
public partial class ActionCenterKeyboardLayoutButton : UserControl
{
private IKeyboardLayout layout;
public event KeyboardLayoutSelectedEventHandler LayoutSelected;
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 Guid LayoutId
{
get { return layout.Id; }
}
public ActionCenterKeyboardLayoutButton(IKeyboardLayout layout)
{
this.layout = layout;
InitializeComponent();
InitializeEvents();
}
private void InitializeEvents()
{
Button.Click += (o, args) => LayoutSelected?.Invoke(layout.Id);
}
}
}

View file

@ -0,0 +1,37 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Desktop.Controls.ActionCenterKeyboardLayoutControl"
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:fa="http://schemas.fontawesome.io/icons/"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Controls"
mc:Ignorable="d" d:DesignHeight="100" d:DesignWidth="125">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Templates/Buttons.xaml" />
<ResourceDictionary Source="../Templates/Colors.xaml" />
<ResourceDictionary Source="../Templates/ScrollViewers.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Background="{StaticResource ActionCenterDarkBrush}" Height="64" Margin="2">
<Popup x:Name="Popup" AllowsTransparency="True" IsOpen="False" Placement="Top" PlacementTarget="{Binding ElementName=Button}">
<Border Background="{StaticResource ActionCenterDarkBrush}">
<ScrollViewer x:Name="LayoutsScrollViewer" MaxHeight="250" VerticalScrollBarVisibility="Auto" Template="{StaticResource SmallBarScrollViewer}">
<StackPanel x:Name="LayoutsStackPanel" />
</ScrollViewer>
</Border>
</Popup>
<Button x:Name="Button" Padding="2" Template="{StaticResource ActionCenterButton}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<fa:ImageAwesome Grid.Row="0" Foreground="Black" Icon="KeyboardOutline" Margin="2" VerticalAlignment="Center" />
<TextBlock Grid.Row="1" x:Name="Text" FontSize="11" Foreground="White" TextAlignment="Center" TextTrimming="CharacterEllipsis" TextWrapping="Wrap" VerticalAlignment="Bottom" />
</Grid>
</Button>
</Grid>
</UserControl>

View file

@ -0,0 +1,81 @@
/*
* Copyright (c) 2019 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.Threading.Tasks;
using System.Windows.Controls;
using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
namespace SafeExamBrowser.UserInterface.Desktop.Controls
{
public partial class ActionCenterKeyboardLayoutControl : UserControl, ISystemKeyboardLayoutControl
{
public event KeyboardLayoutSelectedEventHandler LayoutSelected;
public ActionCenterKeyboardLayoutControl()
{
InitializeComponent();
InitializeKeyboardLayoutControl();
}
public void Add(IKeyboardLayout layout)
{
Dispatcher.Invoke(() =>
{
var button = new ActionCenterKeyboardLayoutButton(layout);
button.LayoutSelected += Button_LayoutSelected;
button.CultureCode = layout.CultureCode;
button.LayoutName = layout.Name;
LayoutsStackPanel.Children.Add(button);
});
}
public void Close()
{
Dispatcher.Invoke(() => Popup.IsOpen = false);
}
public void SetCurrent(IKeyboardLayout layout)
{
Dispatcher.Invoke(() =>
{
foreach (var child in LayoutsStackPanel.Children)
{
if (child is ActionCenterKeyboardLayoutButton layoutButton)
{
layoutButton.IsCurrent = layout.Id == layoutButton.LayoutId;
}
}
Text.Text = layout.Name;
});
}
public void SetTooltip(string text)
{
Dispatcher.Invoke(() => Button.ToolTip = text);
}
private void InitializeKeyboardLayoutControl()
{
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver));
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver));
}
private void Button_LayoutSelected(Guid id)
{
Popup.IsOpen = false;
LayoutSelected?.Invoke(id);
}
}
}

View file

@ -4,7 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Controls" xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Controls"
mc:Ignorable="d" d:DesignHeight="60" d:DesignWidth="80"> mc:Ignorable="d" d:DesignHeight="100" d:DesignWidth="125">
<UserControl.Resources> <UserControl.Resources>
<ResourceDictionary> <ResourceDictionary>
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
@ -13,15 +13,15 @@
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
</ResourceDictionary> </ResourceDictionary>
</UserControl.Resources> </UserControl.Resources>
<Grid Background="#AA808080" Margin="5"> <Grid Background="{StaticResource ActionCenterDarkBrush}" Height="64" Margin="2">
<Button x:Name="IconButton" Click="Icon_Click" Padding="2" Template="{StaticResource ActionCenterButton}"> <Button x:Name="IconButton" Click="Icon_Click" Padding="2" Template="{StaticResource ActionCenterButton}">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="2*" /> <RowDefinition Height="2*" />
<RowDefinition Height="1*" /> <RowDefinition Height="3*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<ContentControl Grid.Row="0" x:Name="Icon" Margin="0,5,0,10" MaxHeight="25" /> <ContentControl Grid.Row="0" x:Name="Icon" Foreground="White" VerticalAlignment="Center" />
<TextBlock Grid.Row="1" x:Name="Text" FontSize="11" Foreground="White" TextAlignment="Left" TextTrimming="CharacterEllipsis" TextWrapping="NoWrap" /> <TextBlock Grid.Row="1" x:Name="Text" FontSize="11" Foreground="White" TextAlignment="Center" TextTrimming="CharacterEllipsis" TextWrapping="Wrap" VerticalAlignment="Bottom" />
</Grid> </Grid>
</Button> </Button>
</Grid> </Grid>

View file

@ -1,4 +1,4 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Desktop.Controls.KeyboardLayoutButton" <UserControl x:Class="SafeExamBrowser.UserInterface.Desktop.Controls.TaskbarKeyboardLayoutButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

View file

@ -6,14 +6,19 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
using System;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
namespace SafeExamBrowser.UserInterface.Desktop.Controls namespace SafeExamBrowser.UserInterface.Desktop.Controls
{ {
public partial class KeyboardLayoutButton : UserControl public partial class TaskbarKeyboardLayoutButton : UserControl
{ {
public event RoutedEventHandler Click; private IKeyboardLayout layout;
public event KeyboardLayoutSelectedEventHandler LayoutSelected;
public string CultureCode public string CultureCode
{ {
@ -30,11 +35,22 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
set { LayoutNameTextBlock.Text = value; } set { LayoutNameTextBlock.Text = value; }
} }
public KeyboardLayoutButton() public Guid LayoutId
{ {
InitializeComponent(); get { return layout.Id; }
}
Button.Click += (o, args) => Click?.Invoke(o, args); public TaskbarKeyboardLayoutButton(IKeyboardLayout layout)
{
this.layout = layout;
InitializeComponent();
InitializeEvents();
}
private void InitializeEvents()
{
Button.Click += (o, args) => LayoutSelected?.Invoke(layout.Id);
} }
} }
} }

View file

@ -1,4 +1,4 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Desktop.Controls.KeyboardLayoutControl" <UserControl x:Class="SafeExamBrowser.UserInterface.Desktop.Controls.TaskbarKeyboardLayoutControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@ -12,16 +12,14 @@
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Templates/Buttons.xaml" /> <ResourceDictionary Source="../Templates/Buttons.xaml" />
<ResourceDictionary Source="../Templates/Colors.xaml" /> <ResourceDictionary Source="../Templates/Colors.xaml" />
<ResourceDictionary Source="../Templates/ScrollViewers.xaml" />
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
</ResourceDictionary> </ResourceDictionary>
</UserControl.Resources> </UserControl.Resources>
<Grid> <Grid>
<Popup x:Name="Popup" IsOpen="False" Placement="Top" PlacementTarget="{Binding ElementName=Button}"> <Popup x:Name="Popup" IsOpen="False" Placement="Top" PlacementTarget="{Binding ElementName=Button}">
<Border Background="LightGray" BorderBrush="Gray" BorderThickness="0.75,0.75,0.75,0"> <Border Background="LightGray" BorderBrush="Gray" BorderThickness="0.75,0.75,0.75,0">
<ScrollViewer x:Name="LayoutsScrollViewer" MaxHeight="250" VerticalScrollBarVisibility="Auto"> <ScrollViewer x:Name="LayoutsScrollViewer" MaxHeight="250" VerticalScrollBarVisibility="Auto" Template="{StaticResource SmallBarScrollViewer}">
<ScrollViewer.Resources>
<s:Double x:Key="{x:Static SystemParameters.VerticalScrollBarWidthKey}">5</s:Double>
</ScrollViewer.Resources>
<StackPanel x:Name="LayoutsStackPanel" /> <StackPanel x:Name="LayoutsStackPanel" />
</ScrollViewer> </ScrollViewer>
</Border> </Border>
@ -34,18 +32,18 @@
</fa:ImageAwesome.Effect> </fa:ImageAwesome.Effect>
</fa:ImageAwesome> </fa:ImageAwesome>
<Viewbox Panel.ZIndex="2" Stretch="Uniform"> <Viewbox Panel.ZIndex="2" Stretch="Uniform">
<StackPanel Orientation="Vertical"> <Grid>
<TextBlock x:Name="LayoutCultureCode" FontWeight="Bold" TextAlignment="Center" Text="ENG"> <Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" x:Name="LayoutCultureCode" FontWeight="Bold" TextAlignment="Center">
<TextBlock.Effect> <TextBlock.Effect>
<DropShadowEffect Color="White" BlurRadius="5" Direction="0" Opacity="1" ShadowDepth="0" /> <DropShadowEffect Color="White" BlurRadius="5" Direction="0" Opacity="1" ShadowDepth="0" />
</TextBlock.Effect> </TextBlock.Effect>
</TextBlock> </TextBlock>
<TextBlock x:Name="LayoutName" Foreground="Gray" TextAlignment="Center" Text="SG"> <TextBlock Grid.Row="1" />
<TextBlock.Effect> </Grid>
<DropShadowEffect Color="White" BlurRadius="5" Direction="0" Opacity="1" ShadowDepth="0" />
</TextBlock.Effect>
</TextBlock>
</StackPanel>
</Viewbox> </Viewbox>
</Grid> </Grid>
</Button> </Button>

View file

@ -17,11 +17,11 @@ using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
namespace SafeExamBrowser.UserInterface.Desktop.Controls namespace SafeExamBrowser.UserInterface.Desktop.Controls
{ {
public partial class KeyboardLayoutControl : UserControl, ISystemKeyboardLayoutControl public partial class TaskbarKeyboardLayoutControl : UserControl, ISystemKeyboardLayoutControl
{ {
public event KeyboardLayoutSelectedEventHandler LayoutSelected; public event KeyboardLayoutSelectedEventHandler LayoutSelected;
public KeyboardLayoutControl() public TaskbarKeyboardLayoutControl()
{ {
InitializeComponent(); InitializeComponent();
InitializeKeyboardLayoutControl(); InitializeKeyboardLayoutControl();
@ -29,33 +29,44 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
public void Add(IKeyboardLayout layout) public void Add(IKeyboardLayout layout)
{ {
var button = new KeyboardLayoutButton(); Dispatcher.Invoke(() =>
button.Click += (o, args) =>
{ {
SetCurrent(button, layout); var button = new TaskbarKeyboardLayoutButton(layout);
Popup.IsOpen = false;
LayoutSelected?.Invoke(layout);
};
button.CultureCode = layout.CultureCode;
button.LayoutName = layout.Name;
LayoutsStackPanel.Children.Add(button); button.LayoutSelected += Button_LayoutSelected;
button.CultureCode = layout.CultureCode;
button.LayoutName = layout.Name;
if (layout.IsCurrent) LayoutsStackPanel.Children.Add(button);
{ });
SetCurrent(button, layout);
}
} }
public void Close() public void Close()
{ {
Popup.IsOpen = false; Dispatcher.Invoke(() => Popup.IsOpen = false);
}
public void SetCurrent(IKeyboardLayout layout)
{
Dispatcher.Invoke(() =>
{
var name = layout.Name?.Length > 3 ? String.Join(string.Empty, layout.Name.Split(' ').Where(s => Char.IsLetter(s.First())).Select(s => s.First())) : layout.Name;
foreach (var child in LayoutsStackPanel.Children)
{
if (child is TaskbarKeyboardLayoutButton layoutButton)
{
layoutButton.IsCurrent = layout.Id == layoutButton.LayoutId;
}
}
LayoutCultureCode.Text = layout.CultureCode;
});
} }
public void SetTooltip(string text) public void SetTooltip(string text)
{ {
Button.ToolTip = text; Dispatcher.Invoke(() => Button.ToolTip = text);
} }
private void InitializeKeyboardLayoutControl() private void InitializeKeyboardLayoutControl()
@ -79,21 +90,10 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
}; };
} }
private void SetCurrent(KeyboardLayoutButton button, IKeyboardLayout layout) private void Button_LayoutSelected(Guid id)
{ {
var name = layout.Name?.Length > 3 ? String.Join(string.Empty, layout.Name.Split(' ').Where(s => Char.IsLetter(s.First())).Select(s => s.First())) : layout.Name; Popup.IsOpen = false;
LayoutSelected?.Invoke(id);
foreach (var child in LayoutsStackPanel.Children)
{
if (child is KeyboardLayoutButton keyboardLayoutButton)
{
keyboardLayoutButton.IsCurrent = false;
}
}
button.IsCurrent = true;
LayoutCultureCode.Text = layout.CultureCode;
LayoutName.Text = name;
} }
} }
} }

View file

@ -83,13 +83,13 @@ namespace SafeExamBrowser.UserInterface.Desktop
} }
logger.Subscribe(model); logger.Subscribe(model);
logger.Info("Opened log window."); logger.Debug("Opened log window.");
} }
private void LogWindow_Closing(object sender, CancelEventArgs e) private void LogWindow_Closing(object sender, CancelEventArgs e)
{ {
logger.Unsubscribe(model); logger.Unsubscribe(model);
logger.Info("Closed log window."); logger.Debug("Closed log window.");
closing?.Invoke(); closing?.Invoke();
} }

View file

@ -80,6 +80,12 @@
<Compile Include="Controls\ActionCenterApplicationButton.xaml.cs"> <Compile Include="Controls\ActionCenterApplicationButton.xaml.cs">
<DependentUpon>ActionCenterApplicationButton.xaml</DependentUpon> <DependentUpon>ActionCenterApplicationButton.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Controls\ActionCenterKeyboardLayoutButton.xaml.cs">
<DependentUpon>ActionCenterKeyboardLayoutButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenterKeyboardLayoutControl.xaml.cs">
<DependentUpon>ActionCenterKeyboardLayoutControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenterNotificationButton.xaml.cs"> <Compile Include="Controls\ActionCenterNotificationButton.xaml.cs">
<DependentUpon>ActionCenterNotificationButton.xaml</DependentUpon> <DependentUpon>ActionCenterNotificationButton.xaml</DependentUpon>
</Compile> </Compile>
@ -92,11 +98,11 @@
<Compile Include="Controls\DateTimeControl.xaml.cs"> <Compile Include="Controls\DateTimeControl.xaml.cs">
<DependentUpon>DateTimeControl.xaml</DependentUpon> <DependentUpon>DateTimeControl.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Controls\KeyboardLayoutButton.xaml.cs"> <Compile Include="Controls\TaskbarKeyboardLayoutButton.xaml.cs">
<DependentUpon>KeyboardLayoutButton.xaml</DependentUpon> <DependentUpon>TaskbarKeyboardLayoutButton.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Controls\KeyboardLayoutControl.xaml.cs"> <Compile Include="Controls\TaskbarKeyboardLayoutControl.xaml.cs">
<DependentUpon>KeyboardLayoutControl.xaml</DependentUpon> <DependentUpon>TaskbarKeyboardLayoutControl.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Controls\TaskbarNotificationButton.xaml.cs"> <Compile Include="Controls\TaskbarNotificationButton.xaml.cs">
<DependentUpon>TaskbarNotificationButton.xaml</DependentUpon> <DependentUpon>TaskbarNotificationButton.xaml</DependentUpon>
@ -154,6 +160,14 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Controls\ActionCenterKeyboardLayoutButton.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\ActionCenterKeyboardLayoutControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\ActionCenterNotificationButton.xaml"> <Page Include="Controls\ActionCenterNotificationButton.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
@ -170,11 +184,11 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Controls\KeyboardLayoutButton.xaml"> <Page Include="Controls\TaskbarKeyboardLayoutButton.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>
<Page Include="Controls\KeyboardLayoutControl.xaml"> <Page Include="Controls\TaskbarKeyboardLayoutControl.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</Page> </Page>

View file

@ -2,4 +2,5 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Templates"> xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Templates">
<SolidColorBrush x:Key="BackgroundBrush">#FFF0F0F0</SolidColorBrush> <SolidColorBrush x:Key="BackgroundBrush">#FFF0F0F0</SolidColorBrush>
<SolidColorBrush x:Key="ActionCenterDarkBrush">#AA808080</SolidColorBrush>
</ResourceDictionary> </ResourceDictionary>

View file

@ -57,9 +57,16 @@ namespace SafeExamBrowser.UserInterface.Desktop
return new BrowserWindow(control, settings, isMainWindow, text); return new BrowserWindow(control, settings, isMainWindow, text);
} }
public ISystemKeyboardLayoutControl CreateKeyboardLayoutControl() public ISystemKeyboardLayoutControl CreateKeyboardLayoutControl(Location location)
{ {
return new KeyboardLayoutControl(); if (location == Location.ActionCenter)
{
return new ActionCenterKeyboardLayoutControl();
}
else
{
return new TaskbarKeyboardLayoutControl();
}
} }
public IWindow CreateLogWindow(ILogger logger) public IWindow CreateLogWindow(ILogger logger)