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();
uiFactoryMock = new Mock<IUserInterfaceFactory>();
taskbarSettings.AllowApplicationLog = true;
taskbarSettings.AllowKeyboardLayout = true;
taskbarSettings.ShowApplicationLog = true;
taskbarSettings.ShowKeyboardLayout = true;
taskbarSettings.AllowWirelessNetwork = true;
taskbarSettings.EnableTaskbar = true;
systemInfoMock.SetupGet(s => s.HasBattery).Returns(true);

View file

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

View file

@ -16,7 +16,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{
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)
{
settings.Taskbar.AllowKeyboardLayout = enabled;
settings.Taskbar.ShowKeyboardLayout = enabled;
}
}

View file

@ -138,17 +138,18 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
settings.ServicePolicy = ServicePolicy.Optional;
settings.Taskbar.AllowApplicationLog = false;
settings.Taskbar.AllowKeyboardLayout = true;
settings.Taskbar.ShowApplicationLog = false;
settings.Taskbar.ShowKeyboardLayout = true;
settings.Taskbar.AllowWirelessNetwork = false;
settings.Taskbar.EnableTaskbar = true;
settings.Taskbar.ShowClock = true;
// 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.MainWindowSettings.AllowAddressBar = true;
settings.Taskbar.AllowApplicationLog = true;
settings.Taskbar.ShowApplicationLog = true;
return settings;
}

View file

@ -16,14 +16,19 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
[Serializable]
public class ActionCenterSettings
{
/// <summary>
/// Determines whether the user may access the application log during runtime.
/// </summary>
public bool AllowApplicationLog { get; set; }
/// <summary>
/// Determines whether the action center itself is enabled and visible to the user.
/// </summary>
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]
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>
/// Determines whether the taskbar itself is enabled and visible to the user.
/// </summary>
public bool EnableTaskbar { get; set; }
/// <summary>
/// Determines whether the application log is accessible via the taskbar.
/// </summary>
public bool ShowApplicationLog { get; set; }
/// <summary>
/// Determines whether the current date and time will be rendered in the taskbar.
/// </summary>
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>
Guid Id { get; }
/// <summary>
/// Specifies whether this is the current keyboard layout.
/// </summary>
bool IsCurrent { get; }
/// <summary>
/// The name of this keyboard layout.
/// </summary>

View file

@ -43,7 +43,7 @@ namespace SafeExamBrowser.Contracts.UserInterface
/// <summary>
/// Creates a system control which allows to change the keyboard layout of the computer.
/// </summary>
ISystemKeyboardLayoutControl CreateKeyboardLayoutControl();
ISystemKeyboardLayoutControl CreateKeyboardLayoutControl(Location location);
/// <summary>
/// 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/.
*/
using SafeExamBrowser.Contracts.SystemComponents;
using System;
namespace SafeExamBrowser.Contracts.UserInterface.Shell.Events
{
/// <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>
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.
/// </summary>
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/.
*/
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows.Forms;
using System.Windows.Input;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.SystemComponents;
@ -18,57 +20,70 @@ namespace SafeExamBrowser.SystemComponents
{
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 InputLanguage originalLanguage;
private CultureInfo originalLanguage;
private IList<ISystemKeyboardLayoutControl> controls;
private IText text;
public KeyboardLayout(ILogger logger, IText text)
{
this.controls = new List<ISystemKeyboardLayoutControl>();
this.layouts = new List<KeyboardLayoutDefinition>();
this.logger = logger;
this.text = text;
}
public void Initialize()
{
originalLanguage = InputLanguage.CurrentInputLanguage;
originalLanguage = InputLanguageManager.Current.CurrentInputLanguage;
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
{
CultureCode = language.Culture.ThreeLetterISOLanguageName.ToUpper(),
IsCurrent = originalLanguage.Equals(language),
Language = language,
Name = language.LayoutName
CultureCode = info.ThreeLetterISOLanguageName.ToUpper(),
CultureInfo = info,
Name = info.NativeName
};
if (originalLanguage.Equals(info))
{
currentLayout = 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)
{
control.LayoutSelected += Control_LayoutSelected;
control.SetTooltip(text.Get(TextKey.SystemControl_KeyboardLayoutTooltip));
foreach (var layout in layouts)
{
control.Add(layout);
}
control.LayoutSelected += Control_LayoutSelected;
control.SetCurrent(currentLayout);
control.SetTooltip(text.Get(TextKey.SystemControl_KeyboardLayoutTooltip));
controls.Add(control);
}
public void Terminate()
{
InputLanguageManager.Current.InputLanguageChanged -= Current_InputLanguageChanged;
if (originalLanguage != null)
{
InputLanguage.CurrentInputLanguage = originalLanguage;
InputLanguageManager.Current.CurrentInputLanguage = 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;
foreach (var l in layouts)
{
l.IsCurrent = l.Id == layout.Id;
}
logger.Info($"Changed keyboard layout to {ToString(language)}.");
InputLanguageManager.Current.CurrentInputLanguage = layout.CultureInfo;
logger.Info($"Changed keyboard layout to {ToString(layout.CultureInfo)}.");
}
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.Windows.Forms;
using System.Globalization;
using SafeExamBrowser.Contracts.SystemComponents;
namespace SafeExamBrowser.SystemComponents
{
internal class KeyboardLayoutDefinition : IKeyboardLayout
{
internal InputLanguage Language { get; set; }
internal CultureInfo CultureInfo { get; set; }
public string CultureCode { get; set; }
public Guid Id { get; }
public bool IsCurrent { get; set; }
public string Name { get; set; }
public KeyboardLayoutDefinition()

View file

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

View file

@ -20,6 +20,6 @@
<ScrollViewer Grid.Row="0" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" Template="{StaticResource SmallBarScrollViewer}">
<StackPanel x:Name="ApplicationPanel" Orientation="Vertical" />
</ScrollViewer>
<UniformGrid x:Name="ControlPanel" Grid.Row="1" Columns="4" Margin="5" />
<UniformGrid x:Name="ControlPanel" Grid.Row="1" Columns="4" Margin="10" />
</Grid>
</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:d="http://schemas.microsoft.com/expression/blend/2008"
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>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
@ -13,15 +13,15 @@
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</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}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="1*" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" x:Name="Icon" Margin="0,5,0,10" MaxHeight="25" />
<TextBlock Grid.Row="1" x:Name="Text" FontSize="11" Foreground="White" TextAlignment="Left" TextTrimming="CharacterEllipsis" TextWrapping="NoWrap" />
<ContentControl Grid.Row="0" x:Name="Icon" Foreground="White" VerticalAlignment="Center" />
<TextBlock Grid.Row="1" x:Name="Text" FontSize="11" Foreground="White" TextAlignment="Center" TextTrimming="CharacterEllipsis" TextWrapping="Wrap" VerticalAlignment="Bottom" />
</Grid>
</Button>
</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:x="http://schemas.microsoft.com/winfx/2006/xaml"
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/.
*/
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 KeyboardLayoutButton : UserControl
public partial class TaskbarKeyboardLayoutButton : UserControl
{
public event RoutedEventHandler Click;
private IKeyboardLayout layout;
public event KeyboardLayoutSelectedEventHandler LayoutSelected;
public string CultureCode
{
@ -30,11 +35,22 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
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:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@ -12,16 +12,14 @@
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Templates/Buttons.xaml" />
<ResourceDictionary Source="../Templates/Colors.xaml" />
<ResourceDictionary Source="../Templates/ScrollViewers.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.75,0.75,0.75,0">
<ScrollViewer x:Name="LayoutsScrollViewer" MaxHeight="250" VerticalScrollBarVisibility="Auto">
<ScrollViewer.Resources>
<s:Double x:Key="{x:Static SystemParameters.VerticalScrollBarWidthKey}">5</s:Double>
</ScrollViewer.Resources>
<ScrollViewer x:Name="LayoutsScrollViewer" MaxHeight="250" VerticalScrollBarVisibility="Auto" Template="{StaticResource SmallBarScrollViewer}">
<StackPanel x:Name="LayoutsStackPanel" />
</ScrollViewer>
</Border>
@ -34,18 +32,18 @@
</fa:ImageAwesome.Effect>
</fa:ImageAwesome>
<Viewbox Panel.ZIndex="2" Stretch="Uniform">
<StackPanel Orientation="Vertical">
<TextBlock x:Name="LayoutCultureCode" FontWeight="Bold" TextAlignment="Center" Text="ENG">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" x:Name="LayoutCultureCode" FontWeight="Bold" TextAlignment="Center">
<TextBlock.Effect>
<DropShadowEffect Color="White" BlurRadius="5" Direction="0" Opacity="1" ShadowDepth="0" />
</TextBlock.Effect>
</TextBlock>
<TextBlock x:Name="LayoutName" Foreground="Gray" TextAlignment="Center" Text="SG">
<TextBlock.Effect>
<DropShadowEffect Color="White" BlurRadius="5" Direction="0" Opacity="1" ShadowDepth="0" />
</TextBlock.Effect>
</TextBlock>
</StackPanel>
<TextBlock Grid.Row="1" />
</Grid>
</Viewbox>
</Grid>
</Button>

View file

@ -17,11 +17,11 @@ using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
namespace SafeExamBrowser.UserInterface.Desktop.Controls
{
public partial class KeyboardLayoutControl : UserControl, ISystemKeyboardLayoutControl
public partial class TaskbarKeyboardLayoutControl : UserControl, ISystemKeyboardLayoutControl
{
public event KeyboardLayoutSelectedEventHandler LayoutSelected;
public KeyboardLayoutControl()
public TaskbarKeyboardLayoutControl()
{
InitializeComponent();
InitializeKeyboardLayoutControl();
@ -29,33 +29,44 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
public void Add(IKeyboardLayout layout)
{
var button = new KeyboardLayoutButton();
button.Click += (o, args) =>
Dispatcher.Invoke(() =>
{
SetCurrent(button, layout);
Popup.IsOpen = false;
LayoutSelected?.Invoke(layout);
};
button.CultureCode = layout.CultureCode;
button.LayoutName = layout.Name;
var button = new TaskbarKeyboardLayoutButton(layout);
LayoutsStackPanel.Children.Add(button);
button.LayoutSelected += Button_LayoutSelected;
button.CultureCode = layout.CultureCode;
button.LayoutName = layout.Name;
if (layout.IsCurrent)
{
SetCurrent(button, layout);
}
LayoutsStackPanel.Children.Add(button);
});
}
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)
{
Button.ToolTip = text;
Dispatcher.Invoke(() => Button.ToolTip = text);
}
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;
foreach (var child in LayoutsStackPanel.Children)
{
if (child is KeyboardLayoutButton keyboardLayoutButton)
{
keyboardLayoutButton.IsCurrent = false;
}
}
button.IsCurrent = true;
LayoutCultureCode.Text = layout.CultureCode;
LayoutName.Text = name;
Popup.IsOpen = false;
LayoutSelected?.Invoke(id);
}
}
}

View file

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

View file

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

View file

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

View file

@ -57,9 +57,16 @@ namespace SafeExamBrowser.UserInterface.Desktop
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)