SEBWIN-141: Implemented scaffolding for touch-optimized user interface.

This commit is contained in:
dbuechel 2019-03-21 16:05:16 +01:00
parent b64a8febe0
commit b7167d35f6
105 changed files with 5179 additions and 242 deletions

View file

@ -23,6 +23,7 @@ using SafeExamBrowser.Contracts.Client;
using SafeExamBrowser.Contracts.Communication.Hosts;
using SafeExamBrowser.Contracts.Communication.Proxies;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.Core.OperationModel;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
@ -42,7 +43,6 @@ using SafeExamBrowser.Monitoring.Mouse;
using SafeExamBrowser.Monitoring.Processes;
using SafeExamBrowser.Monitoring.Windows;
using SafeExamBrowser.SystemComponents;
using SafeExamBrowser.UserInterface.Desktop;
using SafeExamBrowser.WindowsApi;
namespace SafeExamBrowser.Client
@ -54,6 +54,7 @@ namespace SafeExamBrowser.Client
private LogLevel logLevel;
private string runtimeHostUri;
private Guid startupToken;
private UserInterfaceMode uiMode;
private IActionCenter actionCenter;
private IBrowserApplicationController browserController;
@ -87,14 +88,14 @@ namespace SafeExamBrowser.Client
InitializeLogging();
InitializeText();
actionCenter = new ActionCenter();
actionCenter = BuildActionCenter();
keyboardLayout = new KeyboardLayout(new ModuleLogger(logger, nameof(KeyboardLayout)), text);
messageBox = new MessageBox(text);
messageBox = BuildMessageBox();
powerSupply = new PowerSupply(new ModuleLogger(logger, nameof(PowerSupply)), text);
processMonitor = new ProcessMonitor(new ModuleLogger(logger, nameof(ProcessMonitor)), nativeMethods);
uiFactory = new UserInterfaceFactory(text);
uiFactory = BuildUserInterfaceFactory();
runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), new ModuleLogger(logger, nameof(RuntimeProxy)));
taskbar = new Taskbar(new ModuleLogger(logger, nameof(Taskbar)));
taskbar = BuildTaskbar();
windowMonitor = new WindowMonitor(new ModuleLogger(logger, nameof(WindowMonitor)), nativeMethods);
wirelessNetwork = new WirelessNetwork(new ModuleLogger(logger, nameof(WirelessNetwork)), text);
@ -153,7 +154,7 @@ namespace SafeExamBrowser.Client
private void ValidateCommandLineArguments()
{
var args = Environment.GetCommandLineArgs();
var hasFive = args?.Length == 5;
var hasFive = args?.Length >= 5;
if (hasFive)
{
@ -168,6 +169,7 @@ namespace SafeExamBrowser.Client
logLevel = level;
runtimeHostUri = args[3];
startupToken = token;
uiMode = args.Length == 6 && Enum.TryParse(args[5], out uiMode) ? uiMode : UserInterfaceMode.Desktop;
return;
}
@ -286,6 +288,50 @@ namespace SafeExamBrowser.Client
return new WindowMonitorOperation(configuration.Settings.KioskMode, logger, windowMonitor);
}
private IActionCenter BuildActionCenter()
{
switch (uiMode)
{
case UserInterfaceMode.Mobile:
return new UserInterface.Mobile.ActionCenter();
default:
return new UserInterface.Desktop.ActionCenter();
}
}
private IMessageBox BuildMessageBox()
{
switch (uiMode)
{
case UserInterfaceMode.Mobile:
return new UserInterface.Mobile.MessageBox(text);
default:
return new UserInterface.Desktop.MessageBox(text);
}
}
private ITaskbar BuildTaskbar()
{
switch (uiMode)
{
case UserInterfaceMode.Mobile:
return new UserInterface.Mobile.Taskbar(new ModuleLogger(logger, nameof(UserInterface.Mobile.Taskbar)));
default:
return new UserInterface.Desktop.Taskbar(new ModuleLogger(logger, nameof(UserInterface.Desktop.Taskbar)));
}
}
private IUserInterfaceFactory BuildUserInterfaceFactory()
{
switch (uiMode)
{
case UserInterfaceMode.Mobile:
return new UserInterface.Mobile.UserInterfaceFactory(text);
default:
return new UserInterface.Desktop.UserInterfaceFactory(text);
}
}
private void UpdateAppConfig()
{
ClientController.AppConfig = configuration.AppConfig;

View file

@ -160,6 +160,10 @@
<Project>{A502DF54-7169-4647-94BD-18B192924866}</Project>
<Name>SafeExamBrowser.UserInterface.Desktop</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.UserInterface.Mobile\SafeExamBrowser.UserInterface.Mobile.csproj">
<Project>{89bc24dd-ff31-496e-9816-a160b686a3d4}</Project>
<Name>SafeExamBrowser.UserInterface.Mobile</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.WindowsApi\SafeExamBrowser.WindowsApi.csproj">
<Project>{73724659-4150-4792-A94E-42F5F3C1B696}</Project>
<Name>SafeExamBrowser.WindowsApi</Name>

View file

@ -148,6 +148,8 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
settings.Taskbar.EnableTaskbar = true;
settings.Taskbar.ShowClock = true;
settings.UserInterfaceMode = UserInterfaceMode.Desktop;
// TODO: Default values for testing of alpha version only, remove for final release!
settings.ActionCenter.ShowApplicationLog = true;
settings.ActionCenter.ShowKeyboardLayout = true;

View file

@ -105,11 +105,12 @@ namespace SafeExamBrowser.Runtime.Operations
var clientLogLevel = Context.Next.Settings.LogLevel.ToString();
var runtimeHostUri = Context.Next.AppConfig.RuntimeAddress;
var startupToken = Context.Next.StartupToken.ToString("D");
var uiMode = Context.Next.Settings.UserInterfaceMode.ToString();
logger.Info("Starting new client process...");
runtimeHost.AllowConnection = true;
runtimeHost.ClientReady += clientReadyEventHandler;
ClientProcess = processFactory.StartNew(clientExecutable, clientLogFile, clientLogLevel, runtimeHostUri, startupToken);
ClientProcess = processFactory.StartNew(clientExecutable, clientLogFile, clientLogLevel, runtimeHostUri, startupToken, uiMode);
logger.Info("Waiting for client to complete initialization...");
clientReady = clientReadyEvent.WaitOne(timeout_ms);

View file

@ -11,7 +11,6 @@ using System.Windows.Controls;
using SafeExamBrowser.Contracts.Applications;
using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
using SafeExamBrowser.UserInterface.Desktop.Utilities;
namespace SafeExamBrowser.UserInterface.Desktop.Controls
{

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

View file

@ -1,63 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace SafeExamBrowser.UserInterface.Desktop.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SafeExamBrowser.UserInterface.Desktop.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View file

@ -1,117 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -1,26 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace SafeExamBrowser.UserInterface.Desktop.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}

View file

@ -1,7 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

View file

@ -364,27 +364,9 @@
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SafeExamBrowser.Contracts\SafeExamBrowser.Contracts.csproj">
@ -392,9 +374,6 @@
<Name>SafeExamBrowser.Contracts</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Resource Include="Images\Chromium.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Images\LogNotification.ico" />
</ItemGroup>

View file

@ -0,0 +1,38 @@
<Window x:Class="SafeExamBrowser.UserInterface.Mobile.AboutWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile"
mc:Ignorable="d"
Title="Version &amp; License Information" Background="White" Height="325" Width="575" ResizeMode="NoResize" Icon="./Images/SafeExamBrowser.ico"
ShowInTaskbar="False" WindowStartupLocation="CenterScreen">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Image Grid.ColumnSpan="2" Source="pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/SplashScreen.png" Margin="0,5,0,0" />
<TextBlock x:Name="VersionInfo" Grid.Row="0" Grid.Column="1" Foreground="Gray" Margin="25,75,100,10" TextWrapping="Wrap" />
<ScrollViewer Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" VerticalScrollBarVisibility="Auto">
<TextBlock x:Name="MainText" Margin="10" FontSize="10" TextWrapping="Wrap">
This application is subject to the terms of the Mozilla Public License, version 2.0. If a copy of the MPL was not
distributed with this application, you can obtain one at http://mozilla.org/MPL/2.0/.
<LineBreak />
<LineBreak />
<Bold><Underline>CefSharp (.NET bindings for the Chromium Embedded Framework)</Underline></Bold>
<LineBreak />
Copyright © 2010-2019 The CefSharp Authors. All rights reserved.
<LineBreak />
<LineBreak />
<Bold><Underline>CEF (Chromium Embedded Framework)</Underline></Bold>
<LineBreak />
Copyright © 2008-2019 Marshall A. Greenblatt. Portions Copyright © 2006-2009 Google Inc. All rights reserved.
</TextBlock>
</ScrollViewer>
</Grid>
</Window>

View file

@ -0,0 +1,53 @@
/*
* 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.Windows;
using System.Windows.Documents;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.UserInterface.Windows;
using SafeExamBrowser.Contracts.UserInterface.Windows.Events;
namespace SafeExamBrowser.UserInterface.Mobile
{
public partial class AboutWindow : Window, IWindow
{
private AppConfig appConfig;
private IText text;
private WindowClosingEventHandler closing;
event WindowClosingEventHandler IWindow.Closing
{
add { closing += value; }
remove { closing -= value; }
}
public AboutWindow(AppConfig appConfig, IText text)
{
this.appConfig = appConfig;
this.text = text;
InitializeComponent();
InitializeAboutWindow();
}
public void BringToForeground()
{
Activate();
}
private void InitializeAboutWindow()
{
Closing += (o, args) => closing?.Invoke();
VersionInfo.Inlines.Add(new Run($"{text.Get(TextKey.Version)} {appConfig.ProgramVersion}") { FontStyle = FontStyles.Italic });
VersionInfo.Inlines.Add(new LineBreak());
VersionInfo.Inlines.Add(new LineBreak());
VersionInfo.Inlines.Add(new Run(appConfig.ProgramCopyright) { FontSize = 10 });
}
}
}

View file

@ -0,0 +1,29 @@
<Window x:Class="SafeExamBrowser.UserInterface.Mobile.ActionCenter"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile.Controls"
mc:Ignorable="d" Title="ActionCenter" Height="1000" Width="400" Background="#EEF0F0F0" AllowsTransparency="True" FontSize="16"
WindowStyle="None" Topmost="True" ResizeMode="NoResize">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="./Templates/ScrollViewers.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<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="3" Margin="10">
<local:ActionCenterClock x:Name="Clock" />
<local:ActionCenterQuitButton x:Name="QuitButton" />
</UniformGrid>
</Grid>
</Window>

View file

@ -0,0 +1,195 @@
/*
* 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.Media.Animation;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
namespace SafeExamBrowser.UserInterface.Mobile
{
public partial class ActionCenter : Window, IActionCenter
{
public bool ShowClock
{
set { Dispatcher.Invoke(() => Clock.Visibility = value ? Visibility.Visible : Visibility.Collapsed); }
}
public event QuitButtonClickedEventHandler QuitButtonClicked;
public ActionCenter()
{
InitializeComponent();
InitializeActionCenter();
}
public void AddApplicationControl(IApplicationControl control)
{
if (control is UIElement uiElement)
{
ApplicationPanel.Children.Add(uiElement);
}
}
public void AddNotificationControl(INotificationControl control)
{
if (control is UIElement uiElement)
{
ControlPanel.Children.Insert(ControlPanel.Children.Count - 2, uiElement);
}
}
public void AddSystemControl(ISystemControl control)
{
if (control is UIElement uiElement)
{
ControlPanel.Children.Insert(ControlPanel.Children.Count - 2, uiElement);
}
}
public new void Close()
{
Dispatcher.Invoke(base.Close);
}
public new void Hide()
{
Dispatcher.Invoke(HideAnimated);
}
public void InitializeBounds()
{
Dispatcher.Invoke(() =>
{
Height = SystemParameters.WorkArea.Height;
Top = 0;
Left = -Width;
});
}
public void InitializeText(IText text)
{
QuitButton.ToolTip = text.Get(TextKey.Shell_QuitButton);
QuitButton.Text.Text = text.Get(TextKey.Shell_QuitButton);
}
public void Register(IActionCenterActivator activator)
{
activator.Activate += Activator_Activate;
activator.Deactivate += Activator_Deactivate;
activator.Toggle += Activator_Toggle;
}
public new void Show()
{
Dispatcher.Invoke(ShowAnimated);
}
private void HideAnimated()
{
var storyboard = new Storyboard();
var animation = new DoubleAnimation
{
EasingFunction = new CircleEase { EasingMode = EasingMode.EaseOut },
From = 0,
To = -Width,
Duration = new Duration(TimeSpan.FromMilliseconds(500))
};
Storyboard.SetTarget(animation, this);
Storyboard.SetTargetProperty(animation, new PropertyPath(LeftProperty));
storyboard.Children.Add(animation);
storyboard.Completed += HideAnimation_Completed;
storyboard.Begin();
}
private void ShowAnimated()
{
var storyboard = new Storyboard();
var animation = new DoubleAnimation
{
EasingFunction = new CircleEase { EasingMode = EasingMode.EaseOut },
From = -Width,
To = 0,
Duration = new Duration(TimeSpan.FromMilliseconds(500))
};
Storyboard.SetTarget(animation, this);
Storyboard.SetTargetProperty(animation, new PropertyPath(LeftProperty));
InitializeBounds();
base.Show();
storyboard.Children.Add(animation);
storyboard.Completed += ShowAnimation_Completed;
storyboard.Begin();
}
private void ShowAnimation_Completed(object sender, EventArgs e)
{
Activate();
Deactivated += ActionCenter_Deactivated;
}
private void HideAnimation_Completed(object sender, EventArgs e)
{
Deactivated -= ActionCenter_Deactivated;
base.Hide();
}
private void ActionCenter_Deactivated(object sender, EventArgs e)
{
HideAnimated();
}
private void Activator_Activate()
{
Dispatcher.InvokeAsync(() =>
{
if (Visibility != Visibility.Visible)
{
ShowAnimated();
}
});
}
private void Activator_Deactivate()
{
Dispatcher.InvokeAsync(() =>
{
if (Visibility == Visibility.Visible)
{
HideAnimated();
}
});
}
private void Activator_Toggle()
{
Dispatcher.InvokeAsync(() =>
{
if (Visibility != Visibility.Visible)
{
ShowAnimated();
}
else
{
HideAnimated();
}
});
}
private void InitializeActionCenter()
{
QuitButton.Clicked += (args) => QuitButtonClicked?.Invoke(args);
}
}
}

View file

@ -0,0 +1,68 @@
<Window x:Class="SafeExamBrowser.UserInterface.Mobile.BrowserWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:fa="http://schemas.fontawesome.io/icons/"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile.Controls"
mc:Ignorable="d" Title="BrowserWindow" Background="#FFF0F0F0" Height="500" Width="750" MinHeight="250" MinWidth="250" Icon=".\Images\SafeExamBrowser.ico">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="./Templates/Buttons.xaml" />
<ResourceDictionary Source="./Templates/Colors.xaml" />
<ResourceDictionary Source="./Templates/ScrollViewers.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border x:Name="Toolbar" Grid.Row="0" BorderBrush="LightGray" BorderThickness="0,0,0,1" Margin="5,0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0" x:Name="BackwardButton" Height="30" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
<Button Grid.Column="1" x:Name="ForwardButton" Height="30" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
<Button Grid.Column="2" x:Name="ReloadButton" Height="30" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
<Grid Grid.Column="3" Height="25" Margin="5,0">
<TextBox x:Name="UrlTextBox" HorizontalAlignment="Stretch" Padding="5,0" VerticalContentAlignment="Center" />
<fa:ImageAwesome x:Name="LoadingIcon" Foreground="Gray" HorizontalAlignment="Right" Icon="Spinner" Margin="5,3" SpinDuration="1.5" Visibility="Collapsed" />
</Grid>
<Button Grid.Column="4" x:Name="MenuButton" Height="30" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
<Popup x:Name="MenuPopup" IsOpen="False" AllowsTransparency="True" PopupAnimation="Slide" Placement="Custom" PlacementTarget="{Binding ElementName=BrowserControlHost}">
<Border Background="{StaticResource BackgroundBrush}" BorderBrush="LightGray" BorderThickness="1,0,1,1" Width="250">
<StackPanel Orientation="Vertical">
<Grid Height="40">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" x:Name="ZoomText" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Button Grid.Column="1" x:Name="ZoomInButton" Margin="5" Padding="5" Template="{StaticResource BrowserButton}">
<fa:ImageAwesome Icon="SearchPlus" />
</Button>
<Button Grid.Column="2" x:Name="ZoomResetButton" Margin="5" Padding="5" Template="{StaticResource BrowserButton}">
<fa:ImageAwesome Icon="Refresh" />
</Button>
<Button Grid.Column="3" x:Name="ZoomOutButton" Margin="5" Padding="5" Template="{StaticResource BrowserButton}">
<fa:ImageAwesome Icon="SearchMinus" />
</Button>
</Grid>
</StackPanel>
</Border>
</Popup>
</Grid>
</Border>
<WindowsFormsHost Grid.Row="1" x:Name="BrowserControlHost" />
</Grid>
</Window>

View file

@ -0,0 +1,282 @@
/*
* 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.ComponentModel;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.Core;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.Browser;
using SafeExamBrowser.Contracts.UserInterface.Browser.Events;
using SafeExamBrowser.Contracts.UserInterface.Windows;
using SafeExamBrowser.Contracts.UserInterface.Windows.Events;
using SafeExamBrowser.UserInterface.Mobile.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile
{
public partial class BrowserWindow : Window, IBrowserWindow
{
private bool isMainWindow;
private BrowserSettings settings;
private IText text;
private WindowClosingEventHandler closing;
private BrowserWindowSettings WindowSettings
{
get { return isMainWindow ? settings.MainWindowSettings : settings.AdditionalWindowSettings; }
}
public bool CanNavigateBackwards { set => Dispatcher.Invoke(() => BackwardButton.IsEnabled = value); }
public bool CanNavigateForwards { set => Dispatcher.Invoke(() => ForwardButton.IsEnabled = value); }
public event AddressChangedEventHandler AddressChanged;
public event ActionRequestedEventHandler BackwardNavigationRequested;
public event ActionRequestedEventHandler ForwardNavigationRequested;
public event ActionRequestedEventHandler ReloadRequested;
public event ActionRequestedEventHandler ZoomInRequested;
public event ActionRequestedEventHandler ZoomOutRequested;
public event ActionRequestedEventHandler ZoomResetRequested;
event WindowClosingEventHandler IWindow.Closing
{
add { closing += value; }
remove { closing -= value; }
}
public BrowserWindow(IBrowserControl browserControl, BrowserSettings settings, bool isMainWindow, IText text)
{
this.isMainWindow = isMainWindow;
this.settings = settings;
this.text = text;
InitializeComponent();
InitializeBrowserWindow(browserControl);
}
public void BringToForeground()
{
Dispatcher.Invoke(() =>
{
if (WindowState == WindowState.Minimized)
{
WindowState = WindowState.Normal;
}
Activate();
});
}
public new void Close()
{
Dispatcher.Invoke(() =>
{
Closing -= BrowserWindow_Closing;
closing?.Invoke();
base.Close();
});
}
public new void Hide()
{
Dispatcher.Invoke(base.Hide);
}
public new void Show()
{
Dispatcher.Invoke(base.Show);
}
public void UpdateAddress(string url)
{
Dispatcher.Invoke(() => UrlTextBox.Text = url);
}
public void UpdateIcon(IIconResource icon)
{
Dispatcher.InvokeAsync(() => Icon = new BitmapImage(icon.Uri));
}
public void UpdateLoadingState(bool isLoading)
{
Dispatcher.Invoke(() =>
{
LoadingIcon.Visibility = isLoading ? Visibility.Visible : Visibility.Collapsed;
LoadingIcon.Spin = isLoading;
});
}
public void UpdateTitle(string title)
{
Dispatcher.Invoke(() => Title = title);
}
private void BrowserWindow_Closing(object sender, CancelEventArgs e)
{
if (isMainWindow)
{
e.Cancel = true;
}
else
{
closing?.Invoke();
}
}
private void BrowserWindow_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.F5)
{
ReloadRequested?.Invoke();
}
}
private CustomPopupPlacement[] MenuPopup_PlacementCallback(Size popupSize, Size targetSize, Point offset)
{
return new[]
{
new CustomPopupPlacement(new Point(targetSize.Width - Toolbar.Margin.Right - popupSize.Width, -2), PopupPrimaryAxis.None)
};
}
private void SystemParameters_StaticPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(SystemParameters.WorkArea))
{
Dispatcher.InvokeAsync(InitializeBounds);
}
}
private void UrlTextBox_GotMouseCapture(object sender, MouseEventArgs e)
{
if (UrlTextBox.Tag as bool? != true)
{
UrlTextBox.SelectAll();
UrlTextBox.Tag = true;
}
}
private void UrlTextBox_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
AddressChanged?.Invoke(UrlTextBox.Text);
}
}
private void InitializeBrowserWindow(IBrowserControl browserControl)
{
if (browserControl is System.Windows.Forms.Control control)
{
BrowserControlHost.Child = control;
}
RegisterEvents();
InitializeBounds();
ApplySettings();
LoadIcons();
LoadText();
}
private void RegisterEvents()
{
var originalBrush = MenuButton.Background;
BackwardButton.Click += (o, args) => BackwardNavigationRequested?.Invoke();
Closing += BrowserWindow_Closing;
ForwardButton.Click += (o, args) => ForwardNavigationRequested?.Invoke();
MenuButton.Click += (o, args) => MenuPopup.IsOpen = !MenuPopup.IsOpen;
MenuButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = MenuPopup.IsMouseOver));
MenuPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(MenuPopup_PlacementCallback);
MenuPopup.Closed += (o, args) => MenuButton.Background = originalBrush;
MenuPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = IsMouseOver));
MenuPopup.Opened += (o, args) => MenuButton.Background = Brushes.LightGray;
KeyUp += BrowserWindow_KeyUp;
ReloadButton.Click += (o, args) => ReloadRequested?.Invoke();
SystemParameters.StaticPropertyChanged += SystemParameters_StaticPropertyChanged;
UrlTextBox.GotKeyboardFocus += (o, args) => UrlTextBox.SelectAll();
UrlTextBox.GotMouseCapture += UrlTextBox_GotMouseCapture;
UrlTextBox.LostKeyboardFocus += (o, args) => UrlTextBox.Tag = null;
UrlTextBox.LostFocus += (o, args) => UrlTextBox.Tag = null;
UrlTextBox.KeyUp += UrlTextBox_KeyUp;
UrlTextBox.MouseDoubleClick += (o, args) => UrlTextBox.SelectAll();
ZoomInButton.Click += (o, args) => ZoomInRequested?.Invoke();
ZoomOutButton.Click += (o, args) => ZoomOutRequested?.Invoke();
ZoomResetButton.Click += (o, args) => ZoomResetRequested?.Invoke();
}
private void ApplySettings()
{
UrlTextBox.IsEnabled = WindowSettings.AllowAddressBar;
ReloadButton.IsEnabled = WindowSettings.AllowReloading;
ReloadButton.Visibility = WindowSettings.AllowReloading ? Visibility.Visible : Visibility.Collapsed;
BackwardButton.IsEnabled = WindowSettings.AllowBackwardNavigation;
BackwardButton.Visibility = WindowSettings.AllowBackwardNavigation ? Visibility.Visible : Visibility.Collapsed;
ForwardButton.IsEnabled = WindowSettings.AllowForwardNavigation;
ForwardButton.Visibility = WindowSettings.AllowForwardNavigation ? Visibility.Visible : Visibility.Collapsed;
}
private void InitializeBounds()
{
if (isMainWindow)
{
if (WindowSettings.FullScreenMode)
{
Top = 0;
Left = 0;
Height = SystemParameters.WorkArea.Height;
Width = SystemParameters.WorkArea.Width;
ResizeMode = ResizeMode.NoResize;
WindowStyle = WindowStyle.None;
}
else
{
WindowState = WindowState.Maximized;
}
}
else
{
Top = 0;
Left = SystemParameters.WorkArea.Width / 2;
Height = SystemParameters.WorkArea.Height;
Width = SystemParameters.WorkArea.Width / 2;
}
}
private void LoadIcons()
{
var backUri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/NavigateBack.xaml");
var forwardUri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/NavigateForward.xaml");
var menuUri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Menu.xaml");
var reloadUri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Reload.xaml");
var backward = new XamlIconResource(backUri);
var forward = new XamlIconResource(forwardUri);
var menu = new XamlIconResource(menuUri);
var reload = new XamlIconResource(reloadUri);
BackwardButton.Content = IconResourceLoader.Load(backward);
ForwardButton.Content = IconResourceLoader.Load(forward);
MenuButton.Content = IconResourceLoader.Load(menu);
ReloadButton.Content = IconResourceLoader.Load(reload);
}
private void LoadText()
{
ZoomText.Text = text.Get(TextKey.BrowserWindow_ZoomMenuItem);
}
}
}

View file

@ -0,0 +1,24 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenterApplicationButton"
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.Mobile.Controls"
mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800">
<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" Click="Button_Click" Height="60" Padding="10" Template="{StaticResource ActionCenterButton}">
<StackPanel Orientation="Horizontal">
<ContentControl x:Name="Icon" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0,0,10,0" Width="20" />
<TextBlock x:Name="Text" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="5" MaxWidth="350" TextTrimming="CharacterEllipsis" />
</StackPanel>
</Button>
</Grid>
</UserControl>

View file

@ -0,0 +1,66 @@
/*
* 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.Windows;
using System.Windows.Controls;
using SafeExamBrowser.Contracts.Applications;
using SafeExamBrowser.Contracts.Core;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
using SafeExamBrowser.UserInterface.Mobile.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
public partial class ActionCenterApplicationButton : UserControl
{
private IApplicationInfo info;
private IApplicationInstance instance;
internal event ApplicationControlClickedEventHandler Clicked;
public ActionCenterApplicationButton(IApplicationInfo info, IApplicationInstance instance = null)
{
this.info = info;
this.instance = instance;
InitializeComponent();
InitializeApplicationInstanceButton();
}
private void InitializeApplicationInstanceButton()
{
Icon.Content = IconResourceLoader.Load(info.IconResource);
Text.Text = instance?.Name ?? info.Name;
Button.ToolTip = instance?.Name ?? info.Tooltip;
if (instance != null)
{
instance.IconChanged += Instance_IconChanged;
instance.NameChanged += Instance_NameChanged;
}
}
private void Instance_IconChanged(IIconResource icon)
{
Dispatcher.InvokeAsync(() => Icon.Content = IconResourceLoader.Load(icon));
}
private void Instance_NameChanged(string name)
{
Dispatcher.Invoke(() =>
{
Text.Text = name;
Button.ToolTip = name;
});
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Clicked?.Invoke(instance?.Id);
}
}
}

View file

@ -0,0 +1,29 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenterApplicationControl"
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.Mobile.Controls"
mc:Ignorable="d" d:DesignHeight="250" d:DesignWidth="500">
<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>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" x:Name="ApplicationName" Background="#AAD3D3D3" FontWeight="Bold" Padding="5" TextAlignment="Center" />
<ContentControl Grid.Row="1" x:Name="ApplicationButton" />
<StackPanel Grid.Row="2" x:Name="InstancePanel" Orientation="Vertical" />
<Border Grid.Row="3" BorderBrush="LightGray" BorderThickness="0,0,0,1" Margin="75,4" />
</Grid>
</UserControl>

View file

@ -0,0 +1,69 @@
/*
* 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.Windows;
using System.Windows.Controls;
using SafeExamBrowser.Contracts.Applications;
using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
public partial class ActionCenterApplicationControl : UserControl, IApplicationControl
{
private IApplicationInfo info;
public event ApplicationControlClickedEventHandler Clicked;
public ActionCenterApplicationControl(IApplicationInfo info)
{
this.info = info;
InitializeComponent();
InitializeApplicationControl(info);
}
public void RegisterInstance(IApplicationInstance instance)
{
Dispatcher.Invoke(() =>
{
var button = new ActionCenterApplicationButton(info, instance);
button.Clicked += (id) => Clicked?.Invoke(id);
instance.Terminated += (id) => Instance_OnTerminated(id, button);
InstancePanel.Children.Add(button);
ApplicationName.Visibility = Visibility.Visible;
ApplicationButton.Visibility = Visibility.Collapsed;
});
}
private void InitializeApplicationControl(IApplicationInfo info)
{
var button = new ActionCenterApplicationButton(info);
button.Button.Click += (o, args) => Clicked?.Invoke();
ApplicationName.Text = info.Name;
ApplicationButton.Content = button;
}
private void Instance_OnTerminated(InstanceIdentifier id, ActionCenterApplicationButton button)
{
Dispatcher.InvokeAsync(() =>
{
InstancePanel.Children.Remove(button);
if (InstancePanel.Children.Count == 0)
{
ApplicationName.Visibility = Visibility.Collapsed;
ApplicationButton.Visibility = Visibility.Visible;
}
});
}
}
}

View file

@ -0,0 +1,36 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenterClock"
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.Mobile.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.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Background="{StaticResource ActionCenterDarkBrush}" Height="82" Margin="2" ToolTip="{Binding Path=ToolTip}">
<Button x:Name="Button" IsEnabled="False" Padding="2" Template="{StaticResource ActionCenterButton}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<fa:ImageAwesome Grid.Row="0" Foreground="Black" Icon="ClockOutline" Margin="2" VerticalAlignment="Center" />
<Grid Grid.Row="1" Background="Transparent" Margin="5,0" VerticalAlignment="Bottom">
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<TextBlock x:Name="TimeTextBlock" Grid.Row="0" Text="{Binding Path=Time}" Foreground="White" HorizontalAlignment="Center" />
<TextBlock x:Name="DateTextBlock" Grid.Row="1" Text="{Binding Path=Date}" Foreground="White" HorizontalAlignment="Center" />
</Grid>
</Grid>
</Button>
</Grid>
</UserControl>

View file

@ -0,0 +1,32 @@
/*
* 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.Windows.Controls;
using SafeExamBrowser.UserInterface.Mobile.ViewModels;
namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
public partial class ActionCenterClock : UserControl
{
private DateTimeViewModel model;
public ActionCenterClock()
{
InitializeComponent();
InitializeControl();
}
private void InitializeControl()
{
model = new DateTimeViewModel(true);
DataContext = model;
TimeTextBlock.DataContext = model;
DateTextBlock.DataContext = model;
}
}
}

View file

@ -0,0 +1,30 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.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.Mobile.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="60" 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.Mobile.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.Mobile.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.Mobile.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 x:Name="Grid" Background="{StaticResource ActionCenterDarkBrush}" Height="82" Margin="2">
<Popup x:Name="Popup" IsOpen="False" Placement="Top" PlacementTarget="{Binding ElementName=Button}">
<Border Background="Gray">
<ScrollViewer 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" Foreground="White" TextAlignment="Center" TextTrimming="CharacterEllipsis" TextWrapping="Wrap" VerticalAlignment="Bottom" />
</Grid>
</Button>
</Grid>
</UserControl>

View file

@ -0,0 +1,86 @@
/*
* 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 System.Windows.Media;
using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
namespace SafeExamBrowser.UserInterface.Mobile.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 SetInformation(string text)
{
Dispatcher.Invoke(() => Button.ToolTip = text);
}
private void InitializeKeyboardLayoutControl()
{
var originalBrush = Grid.Background;
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));
Popup.Opened += (o, args) => Grid.Background = Brushes.Gray;
Popup.Closed += (o, args) => Grid.Background = originalBrush;
}
private void Button_LayoutSelected(Guid id)
{
Popup.IsOpen = false;
LayoutSelected?.Invoke(id);
}
}
}

View file

@ -0,0 +1,28 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenterNotificationButton"
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.Mobile.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.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Background="{StaticResource ActionCenterDarkBrush}" Height="82" Margin="2">
<Button x:Name="IconButton" Click="Icon_Click" Padding="2" Template="{StaticResource ActionCenterButton}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" x:Name="Icon" VerticalAlignment="Center" />
<TextBlock Grid.Row="1" x:Name="Text" Foreground="White" TextAlignment="Center" TextTrimming="CharacterEllipsis" TextWrapping="Wrap" VerticalAlignment="Bottom" />
</Grid>
</Button>
</Grid>
</UserControl>

View file

@ -0,0 +1,40 @@
/*
* 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.Windows;
using System.Windows.Controls;
using SafeExamBrowser.Contracts.Client;
using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
using SafeExamBrowser.UserInterface.Mobile.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
public partial class ActionCenterNotificationButton : UserControl, INotificationControl
{
public event NotificationControlClickedEventHandler Clicked;
public ActionCenterNotificationButton(INotificationInfo info)
{
InitializeComponent();
InitializeNotificationIcon(info);
}
private void Icon_Click(object sender, RoutedEventArgs e)
{
Clicked?.Invoke();
}
private void InitializeNotificationIcon(INotificationInfo info)
{
Icon.Content = IconResourceLoader.Load(info.IconResource);
IconButton.ToolTip = info.Tooltip;
Text.Text = info.Tooltip;
}
}
}

View file

@ -0,0 +1,51 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenterPowerSupplyControl"
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.Mobile.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.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Background="{StaticResource ActionCenterDarkBrush}" Height="82" Margin="2">
<Button x:Name="Button" IsEnabled="False" Padding="2" ToolTipService.ShowOnDisabled="True" Template="{StaticResource ActionCenterButton}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<Viewbox Grid.Row="0" Stretch="Uniform" Width="Auto">
<Canvas Height="40" Width="40">
<Viewbox Stretch="Uniform" Width="40" Panel.ZIndex="2">
<Canvas Width="1024.000" Height="1024.000">
<Canvas.LayoutTransform>
<RotateTransform Angle="180" />
</Canvas.LayoutTransform>
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 112.000,300.000 L 951.000,300.000 C 970.330,300.000 986.000,315.670 986.000,335.000 L 986.000,689.000 C 986.000,708.330 970.330,724.000 951.000,724.000 L 112.000,724.000 C 92.670,724.000 77.000,708.330 77.000,689.000 L 77.000,335.000 C 77.000,315.670 92.670,300.000 112.000,300.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 25.000,462.500 L 50.000,462.500 C 52.760,462.500 55.000,464.740 55.000,467.500 L 55.000,557.500 C 55.000,560.260 52.760,562.500 50.000,562.500 L 25.000,562.500 C 22.240,562.500 20.000,560.260 20.000,557.500 L 20.000,467.500 C 20.000,464.740 22.240,462.500 25.000,462.500 Z"/>
</Canvas>
</Canvas>
</Viewbox>
<Rectangle x:Name="BatteryCharge" Canvas.Left="2" Canvas.Top="12" Fill="Green" Height="16" Width="35" Panel.ZIndex="1" />
<Canvas x:Name="PowerPlug" Panel.ZIndex="3" Canvas.Left="4" Canvas.Top="-3">
<Canvas.LayoutTransform>
<ScaleTransform ScaleX="2" ScaleY="2" />
</Canvas.LayoutTransform>
<Path Stroke="Black" StrokeStartLineCap="Round" Fill="Black" Data="M2.5,17.5 V10 Q5,10 5,6 H4 V4 H4 V6 H1 V4 H1 V6 H0 Q0,10 2.5,10" />
</Canvas>
<TextBlock x:Name="Warning" FontSize="36" FontWeight="ExtraBold" Foreground="Red" Canvas.Left="13" Canvas.Top="-7" Panel.ZIndex="3">!</TextBlock>
</Canvas>
</Viewbox>
<TextBlock Grid.Row="1" x:Name="Text" FontSize="15" Foreground="White" TextAlignment="Center" TextTrimming="CharacterEllipsis" TextWrapping="WrapWithOverflow" VerticalAlignment="Bottom" />
</Grid>
</Button>
</Grid>
</UserControl>

View file

@ -0,0 +1,67 @@
/*
* 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.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Shell;
namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
public partial class ActionCenterPowerSupplyControl : UserControl, ISystemPowerSupplyControl
{
private double BATTERY_CHARGE_MAX_WIDTH;
public ActionCenterPowerSupplyControl()
{
InitializeComponent();
BATTERY_CHARGE_MAX_WIDTH = BatteryCharge.Width;
}
public void Close()
{
}
public void SetBatteryCharge(double charge, BatteryChargeStatus status)
{
Dispatcher.InvokeAsync(() =>
{
var width = BATTERY_CHARGE_MAX_WIDTH * charge;
width = width > BATTERY_CHARGE_MAX_WIDTH ? BATTERY_CHARGE_MAX_WIDTH : width;
width = width < 0 ? 0 : width;
BatteryCharge.Width = width;
BatteryCharge.Fill = status == BatteryChargeStatus.Low ? Brushes.Orange : BatteryCharge.Fill;
BatteryCharge.Fill = status == BatteryChargeStatus.Critical ? Brushes.Red : BatteryCharge.Fill;
Warning.Visibility = status == BatteryChargeStatus.Critical ? Visibility.Visible : Visibility.Collapsed;
});
}
public void SetPowerGridConnection(bool connected)
{
Dispatcher.InvokeAsync(() => PowerPlug.Visibility = connected ? Visibility.Visible : Visibility.Collapsed);
}
public void SetInformation(string text)
{
Dispatcher.InvokeAsync(() => Text.Text = text);
}
public void ShowCriticalBatteryWarning(string warning)
{
Dispatcher.InvokeAsync(() => Button.ToolTip = warning);
}
public void ShowLowBatteryInfo(string info)
{
Dispatcher.InvokeAsync(() => Button.ToolTip = info);
}
}
}

View file

@ -0,0 +1,28 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenterQuitButton"
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.Mobile.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.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Background="{StaticResource ActionCenterDarkBrush}" Height="82" Margin="2">
<Button x:Name="Button" Padding="2" Template="{StaticResource ActionCenterButton}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" x:Name="Icon" Foreground="Black" VerticalAlignment="Center" />
<TextBlock Grid.Row="1" x:Name="Text" Foreground="White" TextAlignment="Center" TextTrimming="CharacterEllipsis" TextWrapping="Wrap" VerticalAlignment="Bottom" />
</Grid>
</Button>
</Grid>
</UserControl>

View file

@ -0,0 +1,36 @@
/*
* 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.ComponentModel;
using System.Windows.Controls;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
using SafeExamBrowser.UserInterface.Mobile.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
public partial class ActionCenterQuitButton : UserControl
{
public event QuitButtonClickedEventHandler Clicked;
public ActionCenterQuitButton()
{
InitializeComponent();
InitializeControl();
}
private void InitializeControl()
{
var uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/ShutDown.xaml");
var resource = new XamlIconResource(uri);
Icon.Content = IconResourceLoader.Load(resource);
Button.Click += (o, args) => Clicked?.Invoke(new CancelEventArgs());
}
}
}

View file

@ -0,0 +1,30 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenterWirelessNetworkButton"
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.Mobile.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="60" 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="SignalStrengthTextBlock" Grid.Column="1" Foreground="White" HorizontalAlignment="Left" Margin="10,0,5,0" VerticalAlignment="Center" />
<TextBlock x:Name="NetworkNameTextBlock" Grid.Column="2" FontWeight="Bold" HorizontalAlignment="Left" Margin="5,0,10,0" VerticalAlignment="Center" />
</Grid>
</Button>
</Grid>
</UserControl>

View file

@ -0,0 +1,50 @@
/*
* 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.Windows;
using System.Windows.Controls;
using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
public partial class ActionCenterWirelessNetworkButton : UserControl
{
private IWirelessNetwork network;
public bool IsCurrent
{
set { IsCurrentTextBlock.Visibility = value ? Visibility.Visible : Visibility.Hidden; }
}
public string NetworkName
{
set { NetworkNameTextBlock.Text = value; }
}
public int SignalStrength
{
set { SignalStrengthTextBlock.Text = $"{value}%"; }
}
public event WirelessNetworkSelectedEventHandler NetworkSelected;
public ActionCenterWirelessNetworkButton(IWirelessNetwork network)
{
this.network = network;
InitializeComponent();
InitializeEvents();
}
private void InitializeEvents()
{
Button.Click += (o, args) => NetworkSelected?.Invoke(network.Id);
}
}
}

View file

@ -0,0 +1,42 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.ActionCenterWirelessNetworkControl"
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.Mobile.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 x:Name="Grid" Background="{StaticResource ActionCenterDarkBrush}" Height="82" Margin="2">
<Popup x:Name="Popup" IsOpen="False" Placement="Top" PlacementTarget="{Binding ElementName=Button}">
<Border Background="Gray">
<ScrollViewer MaxHeight="250" VerticalScrollBarVisibility="Auto" Template="{StaticResource SmallBarScrollViewer}">
<StackPanel x:Name="NetworksStackPanel" />
</ScrollViewer>
</Border>
</Popup>
<Button x:Name="Button" Padding="2" Template="{StaticResource ActionCenterButton}" ToolTipService.ShowOnDisabled="True">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center">
<Viewbox x:Name="SignalStrengthIcon" Stretch="Uniform" Width="Auto" />
<fa:ImageAwesome x:Name="NoAdapterIcon" Foreground="Red" Icon="Ban" Margin="1" Opacity="0.3" Visibility="Collapsed" />
<fa:ImageAwesome x:Name="LoadingIcon" Foreground="Black" Icon="Cog" Margin="5" Spin="True" SpinDuration="2" Visibility="Collapsed" />
<Image x:Name="NetworkStatusIcon" Height="8" HorizontalAlignment="Right" Margin="-2,0" Panel.ZIndex="10" VerticalAlignment="Bottom" />
</Grid>
<TextBlock Grid.Row="1" x:Name="Text" Foreground="White" TextAlignment="Center" TextTrimming="CharacterEllipsis" TextWrapping="Wrap" VerticalAlignment="Bottom" />
</Grid>
</Button>
</Grid>
</UserControl>

View file

@ -0,0 +1,139 @@
/*
* 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.Collections.Generic;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using FontAwesome.WPF;
using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
using SafeExamBrowser.UserInterface.Mobile.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
public partial class ActionCenterWirelessNetworkControl : UserControl, ISystemWirelessNetworkControl
{
public bool HasWirelessNetworkAdapter
{
set
{
Dispatcher.Invoke(() =>
{
Button.IsEnabled = value;
NoAdapterIcon.Visibility = value ? Visibility.Collapsed : Visibility.Visible;
});
}
}
public bool IsConnecting
{
set
{
Dispatcher.Invoke(() =>
{
LoadingIcon.Visibility = value ? Visibility.Visible : Visibility.Collapsed;
SignalStrengthIcon.Visibility = value ? Visibility.Collapsed : Visibility.Visible;
NetworkStatusIcon.Visibility = value ? Visibility.Collapsed : Visibility.Visible;
});
}
}
public WirelessNetworkStatus NetworkStatus
{
set
{
Dispatcher.Invoke(() =>
{
var icon = value == WirelessNetworkStatus.Connected ? FontAwesomeIcon.Check : FontAwesomeIcon.Close;
var brush = value == WirelessNetworkStatus.Connected ? Brushes.Green : Brushes.Orange;
if (value == WirelessNetworkStatus.Disconnected)
{
SignalStrengthIcon.Child = GetIcon(0);
}
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(icon, brush);
});
}
}
public event WirelessNetworkSelectedEventHandler NetworkSelected;
public ActionCenterWirelessNetworkControl()
{
InitializeComponent();
InitializeWirelessNetworkControl();
}
public void Close()
{
Dispatcher.Invoke(() => Popup.IsOpen = false);
}
public void SetInformation(string text)
{
Dispatcher.Invoke(() =>
{
Button.ToolTip = text;
Text.Text = text;
});
}
public void Update(IEnumerable<IWirelessNetwork> networks)
{
Dispatcher.Invoke(() =>
{
NetworksStackPanel.Children.Clear();
foreach (var network in networks)
{
var button = new ActionCenterWirelessNetworkButton(network);
var isCurrent = network.Status == WirelessNetworkStatus.Connected;
button.IsCurrent = isCurrent;
button.NetworkName = network.Name;
button.SignalStrength = network.SignalStrength;
button.NetworkSelected += (id) => NetworkSelected?.Invoke(id);
if (isCurrent)
{
NetworkStatus = network.Status;
SignalStrengthIcon.Child = GetIcon(network.SignalStrength);
}
NetworksStackPanel.Children.Add(button);
}
});
}
private void InitializeWirelessNetworkControl()
{
var originalBrush = Grid.Background;
SignalStrengthIcon.Child = GetIcon(0);
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));
Popup.Opened += (o, args) => Grid.Background = Brushes.Gray;
Popup.Closed += (o, args) => Grid.Background = originalBrush;
}
private UIElement GetIcon(int signalStrength)
{
var icon = signalStrength > 66 ? "100" : (signalStrength > 33 ? "66" : (signalStrength > 0 ? "33" : "0"));
var uri = new Uri($"pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/WiFi_Light_{icon}.xaml");
var resource = new XamlIconResource(uri);
return IconResourceLoader.Load(resource);
}
}
}

View file

@ -0,0 +1,28 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.TaskbarApplicationControl"
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.Mobile.Controls"
xmlns:s="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d" d:DesignHeight="40" d:DesignWidth="50">
<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>
<Popup x:Name="InstancePopup" IsOpen="False" Placement="Top" PlacementTarget="{Binding ElementName=Button}">
<Border Background="LightGray" BorderBrush="Gray" BorderThickness="0.75,0.75,0.75,0">
<ScrollViewer x:Name="InstanceScrollViewer" MaxHeight="400" VerticalScrollBarVisibility="Auto" Template="{StaticResource SmallBarScrollViewer}">
<StackPanel x:Name="InstanceStackPanel" />
</ScrollViewer>
</Border>
</Popup>
<Button x:Name="Button" Background="{StaticResource BackgroundBrush}" Click="Button_Click" Padding="4" Template="{StaticResource TaskbarButton}" Width="60" />
</Grid>
</UserControl>

View file

@ -0,0 +1,97 @@
/*
* 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.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
using SafeExamBrowser.Contracts.Applications;
using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
using SafeExamBrowser.UserInterface.Mobile.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
public partial class TaskbarApplicationControl : UserControl, IApplicationControl
{
private IApplicationInfo info;
private IList<IApplicationInstance> instances = new List<IApplicationInstance>();
public event ApplicationControlClickedEventHandler Clicked;
public TaskbarApplicationControl(IApplicationInfo info)
{
this.info = info;
InitializeComponent();
InitializeApplicationControl();
}
public void RegisterInstance(IApplicationInstance instance)
{
Dispatcher.Invoke(() =>
{
var instanceButton = new TaskbarApplicationInstanceButton(instance, info);
instanceButton.Clicked += (id) => Clicked?.Invoke(id);
instance.Terminated += (id) => Instance_OnTerminated(id, instanceButton);
instances.Add(instance);
InstanceStackPanel.Children.Add(instanceButton);
});
}
private void InitializeApplicationControl()
{
var originalBrush = Button.Background;
Button.ToolTip = info.Tooltip;
Button.Content = IconResourceLoader.Load(info.IconResource);
Button.MouseEnter += (o, args) => InstancePopup.IsOpen = instances.Count > 1;
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => InstancePopup.IsOpen = InstancePopup.IsMouseOver));
InstancePopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => InstancePopup.IsOpen = IsMouseOver));
InstancePopup.Opened += (o, args) =>
{
Background = Brushes.LightGray;
Button.Background = Brushes.LightGray;
};
InstancePopup.Closed += (o, args) =>
{
Background = originalBrush;
Button.Background = originalBrush;
};
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (instances.Count <= 1)
{
Clicked?.Invoke(instances.FirstOrDefault()?.Id);
}
else
{
InstancePopup.IsOpen = true;
}
}
private void Instance_OnTerminated(InstanceIdentifier id, TaskbarApplicationInstanceButton instanceButton)
{
Dispatcher.InvokeAsync(() =>
{
instances.Remove(instances.FirstOrDefault(i => i.Id == id));
InstanceStackPanel.Children.Remove(instanceButton);
});
}
}
}

View file

@ -0,0 +1,24 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.TaskbarApplicationInstanceButton"
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.Mobile.Controls"
mc:Ignorable="d" 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" Click="Button_Click" Height="60" Padding="10" Template="{StaticResource TaskbarButton}">
<StackPanel Orientation="Horizontal">
<ContentControl x:Name="Icon" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0,0,10,0" Width="20" />
<TextBlock x:Name="Text" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="5" MaxWidth="350" TextTrimming="CharacterEllipsis" />
</StackPanel>
</Button>
</Grid>
</UserControl>

View file

@ -0,0 +1,63 @@
/*
* 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.Windows;
using System.Windows.Controls;
using SafeExamBrowser.Contracts.Applications;
using SafeExamBrowser.Contracts.Core;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
using SafeExamBrowser.UserInterface.Mobile.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
public partial class TaskbarApplicationInstanceButton : UserControl
{
private IApplicationInfo info;
private IApplicationInstance instance;
internal event ApplicationControlClickedEventHandler Clicked;
public TaskbarApplicationInstanceButton(IApplicationInstance instance, IApplicationInfo info)
{
this.info = info;
this.instance = instance;
InitializeComponent();
InitializeApplicationInstanceButton();
}
private void InitializeApplicationInstanceButton()
{
Icon.Content = IconResourceLoader.Load(info.IconResource);
Text.Text = instance.Name;
Button.ToolTip = instance.Name;
instance.IconChanged += Instance_IconChanged;
instance.NameChanged += Instance_NameChanged;
}
private void Instance_IconChanged(IIconResource icon)
{
Dispatcher.InvokeAsync(() => Icon.Content = IconResourceLoader.Load(icon));
}
private void Instance_NameChanged(string name)
{
Dispatcher.Invoke(() =>
{
Text.Text = name;
Button.ToolTip = name;
});
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Clicked?.Invoke(instance.Id);
}
}
}

View file

@ -0,0 +1,17 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.TaskbarClock"
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.Mobile.Controls"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Background="Transparent" Margin="5,0" ToolTip="{Binding Path=ToolTip}">
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<TextBlock x:Name="TimeTextBlock" Grid.Row="0" Text="{Binding Path=Time}" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Bottom" />
<TextBlock x:Name="DateTextBlock" Grid.Row="1" Text="{Binding Path=Date}" HorizontalAlignment="Center" VerticalAlignment="Top" />
</Grid>
</UserControl>

View file

@ -0,0 +1,32 @@
/*
* 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.Windows.Controls;
using SafeExamBrowser.UserInterface.Mobile.ViewModels;
namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
public partial class TaskbarClock : UserControl
{
private DateTimeViewModel model;
public TaskbarClock()
{
InitializeComponent();
InitializeControl();
}
private void InitializeControl()
{
model = new DateTimeViewModel(false);
DataContext = model;
TimeTextBlock.DataContext = model;
DateTextBlock.DataContext = model;
}
}
}

View file

@ -0,0 +1,30 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.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"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile.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="60" 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,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.Mobile.Controls
{
public partial class TaskbarKeyboardLayoutButton : 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 TaskbarKeyboardLayoutButton(IKeyboardLayout layout)
{
this.layout = layout;
InitializeComponent();
InitializeEvents();
}
private void InitializeEvents()
{
Button.Click += (o, args) => LayoutSelected?.Invoke(layout.Id);
}
}
}

View file

@ -0,0 +1,32 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.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"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile.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 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" Template="{StaticResource SmallBarScrollViewer}">
<StackPanel x:Name="LayoutsStackPanel" />
</ScrollViewer>
</Border>
</Popup>
<Button x:Name="Button" Background="Transparent" Template="{StaticResource TaskbarButton}" Padding="5" Width="60">
<Viewbox Stretch="Uniform">
<TextBlock x:Name="LayoutCultureCode" FontWeight="Bold" Margin="2" TextAlignment="Center" VerticalAlignment="Center" />
</Viewbox>
</Button>
</Grid>
</UserControl>

View file

@ -0,0 +1,99 @@
/*
* 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.Linq;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Media;
using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
public partial class TaskbarKeyboardLayoutControl : UserControl, ISystemKeyboardLayoutControl
{
public event KeyboardLayoutSelectedEventHandler LayoutSelected;
public TaskbarKeyboardLayoutControl()
{
InitializeComponent();
InitializeKeyboardLayoutControl();
}
public void Add(IKeyboardLayout layout)
{
Dispatcher.Invoke(() =>
{
var button = new TaskbarKeyboardLayoutButton(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(() =>
{
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 SetInformation(string text)
{
Dispatcher.Invoke(() => Button.ToolTip = text);
}
private void InitializeKeyboardLayoutControl()
{
var originalBrush = Button.Background;
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));
Popup.Opened += (o, args) =>
{
Background = Brushes.LightGray;
Button.Background = Brushes.LightGray;
};
Popup.Closed += (o, args) =>
{
Background = originalBrush;
Button.Background = originalBrush;
};
}
private void Button_LayoutSelected(Guid id)
{
Popup.IsOpen = false;
LayoutSelected?.Invoke(id);
}
}
}

View file

@ -0,0 +1,19 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.TaskbarNotificationButton"
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.Mobile.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>
<Button x:Name="IconButton" Background="{StaticResource BackgroundBrush}" Click="Icon_Click" Padding="7.5" Template="{StaticResource TaskbarButton}" Width="60" />
</Grid>
</UserControl>

View file

@ -0,0 +1,39 @@
/*
* 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.Windows;
using System.Windows.Controls;
using SafeExamBrowser.Contracts.Client;
using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
using SafeExamBrowser.UserInterface.Mobile.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
public partial class TaskbarNotificationButton : UserControl, INotificationControl
{
public event NotificationControlClickedEventHandler Clicked;
public TaskbarNotificationButton(INotificationInfo info)
{
InitializeComponent();
InitializeNotificationIcon(info);
}
private void Icon_Click(object sender, RoutedEventArgs e)
{
Clicked?.Invoke();
}
private void InitializeNotificationIcon(INotificationInfo info)
{
IconButton.ToolTip = info.Tooltip;
IconButton.Content = IconResourceLoader.Load(info.IconResource);
}
}
}

View file

@ -0,0 +1,63 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.TaskbarPowerSupplyControl"
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.Mobile.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.75,0.75,0.75,0">
<Grid MaxWidth="250" Margin="20,10,20,20">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Grid.Column="1" Background="Transparent" BorderBrush="LightGray" Click="Button_Click" Cursor="Hand" FontWeight="Bold"
Foreground="Gray" HorizontalAlignment="Center" Margin="0,0,0,5" Width="20">X</Button>
</Grid>
<TextBlock Grid.Row="1" x:Name="PopupText" TextWrapping="Wrap" />
</Grid>
</Border>
</Popup>
<Button x:Name="Button" Background="Transparent" IsEnabled="False" Padding="5" Template="{StaticResource TaskbarButton}" ToolTipService.ShowOnDisabled="True">
<Viewbox Stretch="Uniform" Width="Auto">
<Canvas Height="40" Width="40">
<Viewbox Stretch="Uniform" Width="40" Panel.ZIndex="2">
<Canvas Width="1024.000" Height="1024.000">
<Canvas.LayoutTransform>
<RotateTransform Angle="180" />
</Canvas.LayoutTransform>
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 112.000,300.000 L 951.000,300.000 C 970.330,300.000 986.000,315.670 986.000,335.000 L 986.000,689.000 C 986.000,708.330 970.330,724.000 951.000,724.000 L 112.000,724.000 C 92.670,724.000 77.000,708.330 77.000,689.000 L 77.000,335.000 C 77.000,315.670 92.670,300.000 112.000,300.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 25.000,462.500 L 50.000,462.500 C 52.760,462.500 55.000,464.740 55.000,467.500 L 55.000,557.500 C 55.000,560.260 52.760,562.500 50.000,562.500 L 25.000,562.500 C 22.240,562.500 20.000,560.260 20.000,557.500 L 20.000,467.500 C 20.000,464.740 22.240,462.500 25.000,462.500 Z"/>
</Canvas>
</Canvas>
</Viewbox>
<Rectangle x:Name="BatteryCharge" Canvas.Left="2" Canvas.Top="12" Fill="Green" Height="16" Width="35" Panel.ZIndex="1" />
<Canvas x:Name="PowerPlug" Panel.ZIndex="3" Canvas.Left="4" Canvas.Top="-3">
<Canvas.LayoutTransform>
<ScaleTransform ScaleX="2" ScaleY="2" />
</Canvas.LayoutTransform>
<Path Stroke="Black" StrokeStartLineCap="Round" Fill="Black" Data="M2.5,17.5 V10 Q5,10 5,6 H4 V4 H4 V6 H1 V4 H1 V6 H0 Q0,10 2.5,10" />
</Canvas>
<TextBlock x:Name="Warning" FontSize="36" FontWeight="ExtraBold" Foreground="Red" Canvas.Left="13" Canvas.Top="-7" Panel.ZIndex="3">!</TextBlock>
</Canvas>
</Viewbox>
</Button>
</Grid>
</UserControl>

View file

@ -0,0 +1,82 @@
/*
* 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.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Shell;
namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
public partial class TaskbarPowerSupplyControl : UserControl, ISystemPowerSupplyControl
{
private double BATTERY_CHARGE_MAX_WIDTH;
public TaskbarPowerSupplyControl()
{
InitializeComponent();
BATTERY_CHARGE_MAX_WIDTH = BatteryCharge.Width;
}
public void Close()
{
Popup.IsOpen = false;
}
public void SetBatteryCharge(double charge, BatteryChargeStatus status)
{
Dispatcher.InvokeAsync(() =>
{
var width = BATTERY_CHARGE_MAX_WIDTH * charge;
width = width > BATTERY_CHARGE_MAX_WIDTH ? BATTERY_CHARGE_MAX_WIDTH : width;
width = width < 0 ? 0 : width;
BatteryCharge.Width = width;
BatteryCharge.Fill = status == BatteryChargeStatus.Low ? Brushes.Orange : BatteryCharge.Fill;
BatteryCharge.Fill = status == BatteryChargeStatus.Critical ? Brushes.Red : BatteryCharge.Fill;
Warning.Visibility = status == BatteryChargeStatus.Critical ? Visibility.Visible : Visibility.Collapsed;
});
}
public void SetPowerGridConnection(bool connected)
{
Dispatcher.InvokeAsync(() => PowerPlug.Visibility = connected ? Visibility.Visible : Visibility.Collapsed);
}
public void SetInformation(string text)
{
Dispatcher.InvokeAsync(() => Button.ToolTip = text);
}
public void ShowCriticalBatteryWarning(string warning)
{
Dispatcher.InvokeAsync(() => ShowPopup(warning));
}
public void ShowLowBatteryInfo(string info)
{
Dispatcher.InvokeAsync(() => ShowPopup(info));
}
private void ShowPopup(string text)
{
Popup.IsOpen = true;
PopupText.Text = text;
Background = Brushes.LightGray;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Popup.IsOpen = false;
Background = Brushes.Transparent;
}
}
}

View file

@ -0,0 +1,20 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.TaskbarQuitButton"
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.Mobile.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>
<Button x:Name="Button" Click="Button_Click" Background="{StaticResource BackgroundBrush}" HorizontalAlignment="Stretch"
Template="{StaticResource TaskbarButton}" VerticalAlignment="Stretch" />
</Grid>
</UserControl>

View file

@ -0,0 +1,41 @@
/*
* 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.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
using SafeExamBrowser.UserInterface.Mobile.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
public partial class TaskbarQuitButton : UserControl
{
public event QuitButtonClickedEventHandler Clicked;
public TaskbarQuitButton()
{
InitializeComponent();
LoadIcon();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Clicked?.Invoke(new CancelEventArgs());
}
private void LoadIcon()
{
var uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/ShutDown.xaml");
var resource = new XamlIconResource(uri);
Button.Content = IconResourceLoader.Load(resource);
}
}
}

View file

@ -0,0 +1,30 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.TaskbarWirelessNetworkButton"
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.Mobile.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="60" 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="SignalStrengthTextBlock" Grid.Column="1" Foreground="Gray" HorizontalAlignment="Left" Margin="10,0,5,0" VerticalAlignment="Center" />
<TextBlock x:Name="NetworkNameTextBlock" Grid.Column="2" FontWeight="Bold" HorizontalAlignment="Left" Margin="5,0,10,0" VerticalAlignment="Center" />
</Grid>
</Button>
</Grid>
</UserControl>

View file

@ -0,0 +1,50 @@
/*
* 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.Windows;
using System.Windows.Controls;
using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
public partial class TaskbarWirelessNetworkButton : UserControl
{
private readonly IWirelessNetwork network;
public bool IsCurrent
{
set { IsCurrentTextBlock.Visibility = value ? Visibility.Visible : Visibility.Hidden; }
}
public string NetworkName
{
set { NetworkNameTextBlock.Text = value; }
}
public int SignalStrength
{
set { SignalStrengthTextBlock.Text = $"{value}%"; }
}
public event WirelessNetworkSelectedEventHandler NetworkSelected;
public TaskbarWirelessNetworkButton(IWirelessNetwork network)
{
this.network = network;
InitializeComponent();
InitializeEvents();
}
private void InitializeEvents()
{
Button.Click += (o, args) => NetworkSelected?.Invoke(network.Id);
}
}
}

View file

@ -0,0 +1,36 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.TaskbarWirelessNetworkControl"
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:s="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile.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 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 MaxHeight="250" VerticalScrollBarVisibility="Auto" Template="{StaticResource SmallBarScrollViewer}">
<StackPanel x:Name="NetworksStackPanel" />
</ScrollViewer>
</Border>
</Popup>
<Button x:Name="Button" Background="Transparent" Padding="5" Template="{StaticResource TaskbarButton}" ToolTipService.ShowOnDisabled="True" Width="60">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Viewbox x:Name="SignalStrengthIcon" Stretch="Uniform" Width="Auto" />
<fa:ImageAwesome x:Name="NoAdapterIcon" Foreground="Red" Icon="Ban" Margin="1" Opacity="0.3" Visibility="Collapsed" />
<fa:ImageAwesome x:Name="LoadingIcon" Foreground="Gray" Icon="Cog" Margin="5" Spin="True" SpinDuration="2" Visibility="Collapsed" />
<Image x:Name="NetworkStatusIcon" Height="12" HorizontalAlignment="Right" Margin="0,2" Panel.ZIndex="10" VerticalAlignment="Bottom" />
</Grid>
</Button>
</Grid>
</UserControl>

View file

@ -0,0 +1,145 @@
/*
* 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.Collections.Generic;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using FontAwesome.WPF;
using SafeExamBrowser.Contracts.SystemComponents;
using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
using SafeExamBrowser.UserInterface.Mobile.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
public partial class TaskbarWirelessNetworkControl : UserControl, ISystemWirelessNetworkControl
{
public bool HasWirelessNetworkAdapter
{
set
{
Dispatcher.InvokeAsync(() =>
{
Button.IsEnabled = value;
NoAdapterIcon.Visibility = value ? Visibility.Collapsed : Visibility.Visible;
});
}
}
public bool IsConnecting
{
set
{
Dispatcher.Invoke(() =>
{
LoadingIcon.Visibility = value ? Visibility.Visible : Visibility.Collapsed;
SignalStrengthIcon.Visibility = value ? Visibility.Collapsed : Visibility.Visible;
NetworkStatusIcon.Visibility = value ? Visibility.Collapsed : Visibility.Visible;
});
}
}
public WirelessNetworkStatus NetworkStatus
{
set
{
Dispatcher.InvokeAsync(() =>
{
var icon = value == WirelessNetworkStatus.Connected ? FontAwesomeIcon.Check : FontAwesomeIcon.Close;
var brush = value == WirelessNetworkStatus.Connected ? Brushes.Green : Brushes.Orange;
if (value == WirelessNetworkStatus.Disconnected)
{
SignalStrengthIcon.Child = GetIcon(0);
}
NetworkStatusIcon.Source = ImageAwesome.CreateImageSource(icon, brush);
});
}
}
public event WirelessNetworkSelectedEventHandler NetworkSelected;
public TaskbarWirelessNetworkControl()
{
InitializeComponent();
InitializeWirelessNetworkControl();
}
public void Close()
{
Popup.IsOpen = false;
}
public void SetInformation(string text)
{
Dispatcher.InvokeAsync(() => Button.ToolTip = text);
}
public void Update(IEnumerable<IWirelessNetwork> networks)
{
Dispatcher.InvokeAsync(() =>
{
NetworksStackPanel.Children.Clear();
foreach (var network in networks)
{
var button = new TaskbarWirelessNetworkButton(network);
var isCurrent = network.Status == WirelessNetworkStatus.Connected;
button.IsCurrent = isCurrent;
button.NetworkName = network.Name;
button.SignalStrength = network.SignalStrength;
button.NetworkSelected += (id) => NetworkSelected?.Invoke(id);
if (isCurrent)
{
NetworkStatus = network.Status;
SignalStrengthIcon.Child = GetIcon(network.SignalStrength);
}
NetworksStackPanel.Children.Add(button);
}
});
}
private void InitializeWirelessNetworkControl()
{
var originalBrush = Button.Background;
SignalStrengthIcon.Child = GetIcon(0);
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));
Popup.Opened += (o, args) =>
{
Background = Brushes.LightGray;
Button.Background = Brushes.LightGray;
};
Popup.Closed += (o, args) =>
{
Background = originalBrush;
Button.Background = originalBrush;
};
}
private UIElement GetIcon(int signalStrength)
{
var icon = signalStrength > 66 ? "100" : (signalStrength > 33 ? "66" : (signalStrength > 0 ? "33" : "0"));
var uri = new Uri($"pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/WiFi_{icon}.xaml");
var resource = new XamlIconResource(uri);
return IconResourceLoader.Load(resource);
}
}
}

View file

@ -0,0 +1,14 @@
<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">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 117.000,512.000 C 117.000,293.840 293.840,117.000 512.000,117.000 C 730.160,117.000 907.000,293.840 907.000,512.000 C 907.000,730.160 730.160,907.000 512.000,907.000 C 293.840,907.000 117.000,730.160 117.000,512.000 Z"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 619.210,298.700"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 619.210,727.560"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 512.000,458.096 L 512.000,741.560"/>
<Path Fill="#ff000000" Data=" M 547.625,312.700 C 547.625,332.375 531.675,348.325 512.000,348.325 C 492.325,348.325 476.375,332.375 476.375,312.700 C 476.375,293.025 492.325,277.075 512.000,277.075 C 531.675,277.075 547.625,293.025 547.625,312.700 Z"/>
</Canvas>
</Canvas>
</Viewbox>

View file

@ -0,0 +1,11 @@
<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">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 112.000,300.000 L 951.000,300.000 C 970.330,300.000 986.000,315.670 986.000,335.000 L 986.000,689.000 C 986.000,708.330 970.330,724.000 951.000,724.000 L 112.000,724.000 C 92.670,724.000 77.000,708.330 77.000,689.000 L 77.000,335.000 C 77.000,315.670 92.670,300.000 112.000,300.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 25.000,462.500 L 50.000,462.500 C 52.760,462.500 55.000,464.740 55.000,467.500 L 55.000,557.500 C 55.000,560.260 52.760,562.500 50.000,562.500 L 25.000,562.500 C 22.240,562.500 20.000,560.260 20.000,557.500 L 20.000,467.500 C 20.000,464.740 22.240,462.500 25.000,462.500 Z"/>
</Canvas>
</Canvas>
</Viewbox>

View file

@ -0,0 +1,34 @@
<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">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 134.000,339.000 L 889.000,339.000 C 908.330,339.000 924.000,354.670 924.000,374.000 L 924.000,649.000 C 924.000,668.330 908.330,684.000 889.000,684.000 L 134.000,684.000 C 114.670,684.000 99.000,668.330 99.000,649.000 L 99.000,374.000 C 99.000,354.670 114.670,339.000 134.000,339.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 152.000,582.000 L 192.000,582.000 C 194.760,582.000 197.000,584.240 197.000,587.000 L 197.000,627.000 C 197.000,629.760 194.760,632.000 192.000,632.000 L 152.000,632.000 C 149.240,632.000 147.000,629.760 147.000,627.000 L 147.000,587.000 C 147.000,584.240 149.240,582.000 152.000,582.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 747.000,582.000 L 787.000,582.000 C 789.760,582.000 792.000,584.240 792.000,587.000 L 792.000,627.000 C 792.000,629.760 789.760,632.000 787.000,632.000 L 747.000,632.000 C 744.240,632.000 742.000,629.760 742.000,627.000 L 742.000,587.000 C 742.000,584.240 744.240,582.000 747.000,582.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 152.000,487.000 L 192.000,487.000 C 194.760,487.000 197.000,489.240 197.000,492.000 L 197.000,532.000 C 197.000,534.760 194.760,537.000 192.000,537.000 L 152.000,537.000 C 149.240,537.000 147.000,534.760 147.000,532.000 L 147.000,492.000 C 147.000,489.240 149.240,487.000 152.000,487.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 322.000,487.000 L 362.000,487.000 C 364.760,487.000 367.000,489.240 367.000,492.000 L 367.000,532.000 C 367.000,534.760 364.760,537.000 362.000,537.000 L 322.000,537.000 C 319.240,537.000 317.000,534.760 317.000,532.000 L 317.000,492.000 C 317.000,489.240 319.240,487.000 322.000,487.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 492.000,487.000 L 532.000,487.000 C 534.760,487.000 537.000,489.240 537.000,492.000 L 537.000,532.000 C 537.000,534.760 534.760,537.000 532.000,537.000 L 492.000,537.000 C 489.240,537.000 487.000,534.760 487.000,532.000 L 487.000,492.000 C 487.000,489.240 489.240,487.000 492.000,487.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 747.000,487.000 L 787.000,487.000 C 789.760,487.000 792.000,489.240 792.000,492.000 L 792.000,532.000 C 792.000,534.760 789.760,537.000 787.000,537.000 L 747.000,537.000 C 744.240,537.000 742.000,534.760 742.000,532.000 L 742.000,492.000 C 742.000,489.240 744.240,487.000 747.000,487.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 152.000,392.000 L 192.000,392.000 C 194.760,392.000 197.000,394.240 197.000,397.000 L 197.000,437.000 C 197.000,439.760 194.760,442.000 192.000,442.000 L 152.000,442.000 C 149.240,442.000 147.000,439.760 147.000,437.000 L 147.000,397.000 C 147.000,394.240 149.240,392.000 152.000,392.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 322.000,392.000 L 362.000,392.000 C 364.760,392.000 367.000,394.240 367.000,397.000 L 367.000,437.000 C 367.000,439.760 364.760,442.000 362.000,442.000 L 322.000,442.000 C 319.240,442.000 317.000,439.760 317.000,437.000 L 317.000,397.000 C 317.000,394.240 319.240,392.000 322.000,392.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 492.000,392.000 L 532.000,392.000 C 534.760,392.000 537.000,394.240 537.000,397.000 L 537.000,437.000 C 537.000,439.760 534.760,442.000 532.000,442.000 L 492.000,442.000 C 489.240,442.000 487.000,439.760 487.000,437.000 L 487.000,397.000 C 487.000,394.240 489.240,392.000 492.000,392.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 747.000,392.000 L 787.000,392.000 C 789.760,392.000 792.000,394.240 792.000,397.000 L 792.000,437.000 C 792.000,439.760 789.760,442.000 787.000,442.000 L 747.000,442.000 C 744.240,442.000 742.000,439.760 742.000,437.000 L 742.000,397.000 C 742.000,394.240 744.240,392.000 747.000,392.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 237.000,582.000 L 277.000,582.000 C 279.760,582.000 282.000,584.240 282.000,587.000 L 282.000,627.000 C 282.000,629.760 279.760,632.000 277.000,632.000 L 237.000,632.000 C 234.240,632.000 232.000,629.760 232.000,627.000 L 232.000,587.000 C 232.000,584.240 234.240,582.000 237.000,582.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 322.000,582.000 L 702.000,582.000 C 704.760,582.000 707.000,584.240 707.000,587.000 L 707.000,627.000 C 707.000,629.760 704.760,632.000 702.000,632.000 L 322.000,632.000 C 319.240,632.000 317.000,629.760 317.000,627.000 L 317.000,587.000 C 317.000,584.240 319.240,582.000 322.000,582.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 832.000,582.000 L 872.000,582.000 C 874.760,582.000 877.000,584.240 877.000,587.000 L 877.000,627.000 C 877.000,629.760 874.760,632.000 872.000,632.000 L 832.000,632.000 C 829.240,632.000 827.000,629.760 827.000,627.000 L 827.000,587.000 C 827.000,584.240 829.240,582.000 832.000,582.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 237.000,487.000 L 277.000,487.000 C 279.760,487.000 282.000,489.240 282.000,492.000 L 282.000,532.000 C 282.000,534.760 279.760,537.000 277.000,537.000 L 237.000,537.000 C 234.240,537.000 232.000,534.760 232.000,532.000 L 232.000,492.000 C 232.000,489.240 234.240,487.000 237.000,487.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 407.000,487.000 L 447.000,487.000 C 449.760,487.000 452.000,489.240 452.000,492.000 L 452.000,532.000 C 452.000,534.760 449.760,537.000 447.000,537.000 L 407.000,537.000 C 404.240,537.000 402.000,534.760 402.000,532.000 L 402.000,492.000 C 402.000,489.240 404.240,487.000 407.000,487.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 577.000,487.000 L 617.000,487.000 C 619.760,487.000 622.000,489.240 622.000,492.000 L 622.000,532.000 C 622.000,534.760 619.760,537.000 617.000,537.000 L 577.000,537.000 C 574.240,537.000 572.000,534.760 572.000,532.000 L 572.000,492.000 C 572.000,489.240 574.240,487.000 577.000,487.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 662.000,487.000 L 702.000,487.000 C 704.760,487.000 707.000,489.240 707.000,492.000 L 707.000,532.000 C 707.000,534.760 704.760,537.000 702.000,537.000 L 662.000,537.000 C 659.240,537.000 657.000,534.760 657.000,532.000 L 657.000,492.000 C 657.000,489.240 659.240,487.000 662.000,487.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 832.000,487.000 L 872.000,487.000 C 874.760,487.000 877.000,489.240 877.000,492.000 L 877.000,532.000 C 877.000,534.760 874.760,537.000 872.000,537.000 L 832.000,537.000 C 829.240,537.000 827.000,534.760 827.000,532.000 L 827.000,492.000 C 827.000,489.240 829.240,487.000 832.000,487.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 237.000,392.000 L 277.000,392.000 C 279.760,392.000 282.000,394.240 282.000,397.000 L 282.000,437.000 C 282.000,439.760 279.760,442.000 277.000,442.000 L 237.000,442.000 C 234.240,442.000 232.000,439.760 232.000,437.000 L 232.000,397.000 C 232.000,394.240 234.240,392.000 237.000,392.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 407.000,392.000 L 447.000,392.000 C 449.760,392.000 452.000,394.240 452.000,397.000 L 452.000,437.000 C 452.000,439.760 449.760,442.000 447.000,442.000 L 407.000,442.000 C 404.240,442.000 402.000,439.760 402.000,437.000 L 402.000,397.000 C 402.000,394.240 404.240,392.000 407.000,392.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 577.000,392.000 L 617.000,392.000 C 619.760,392.000 622.000,394.240 622.000,397.000 L 622.000,437.000 C 622.000,439.760 619.760,442.000 617.000,442.000 L 577.000,442.000 C 574.240,442.000 572.000,439.760 572.000,437.000 L 572.000,397.000 C 572.000,394.240 574.240,392.000 577.000,392.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 662.000,392.000 L 702.000,392.000 C 704.760,392.000 707.000,394.240 707.000,397.000 L 707.000,437.000 C 707.000,439.760 704.760,442.000 702.000,442.000 L 662.000,442.000 C 659.240,442.000 657.000,439.760 657.000,437.000 L 657.000,397.000 C 657.000,394.240 659.240,392.000 662.000,392.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 832.000,392.000 L 872.000,392.000 C 874.760,392.000 877.000,394.240 877.000,397.000 L 877.000,437.000 C 877.000,439.760 874.760,442.000 872.000,442.000 L 832.000,442.000 C 829.240,442.000 827.000,439.760 827.000,437.000 L 827.000,397.000 C 827.000,394.240 829.240,392.000 832.000,392.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 668.000,201.000 L 512.000,90.000 L 668.000,201.000 Z M 356.000,201.000 L 512.000,90.000 L 356.000,201.000 Z"/>
</Canvas>
</Canvas>
</Viewbox>

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

View file

@ -0,0 +1,7 @@
<Viewbox Stretch="Uniform"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:fa="http://schemas.fontawesome.io/icons/">
<Canvas Height="120" Width="120">
<Path Stroke="Black" StrokeLineJoin="Round" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeThickness="8" Data="M 20,30 L 100,30 M 20,60 L 100,60 M 20,90 L 100,90" />
</Canvas>
</Viewbox>

View file

@ -0,0 +1,12 @@
<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">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 753.838,835.919 L 268.162,510.424 L 753.838,188.341"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 766.763,835.919"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 766.763,188.341"/>
</Canvas>
</Canvas>
</Viewbox>

View file

@ -0,0 +1,12 @@
<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">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 268.162,835.919 L 753.838,510.424 L 268.162,188.341"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 766.763,835.919"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 766.763,188.341"/>
</Canvas>
</Canvas>
</Viewbox>

View file

@ -0,0 +1,11 @@
<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">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 862.000,557.000 C 862.000,750.300 705.300,907.000 512.000,907.000 C 318.700,907.000 162.000,750.300 162.000,557.000 C 162.000,363.700 318.700,207.000 512.000,207.000"/>
<Path Fill="#ff000000" Data="F1 M 471.000,67.000 L 712.500,207.000 L 471.000,345.000 L 471.000,67.000 Z"/>
</Canvas>
</Canvas>
</Viewbox>

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

View file

@ -0,0 +1,12 @@
<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">
<Canvas>
<Path StrokeThickness="79.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 512.000,902.000 C 296.610,902.000 122.000,727.280 122.000,511.740 C 122.000,388.110 179.450,277.910 269.090,206.400"/>
<Path StrokeThickness="79.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 512.000,902.000 C 727.390,902.000 902.000,727.280 902.000,511.740 C 902.000,388.110 844.550,277.910 754.910,206.400"/>
<Path StrokeThickness="79.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 512.560,112.000 L 512.560,494.460"/>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
</Canvas>
</Canvas>
</Viewbox>

View file

@ -0,0 +1,11 @@
<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">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 301.086,510.424 L 786.763,188.341 L 786.763,835.919 L 301.086,510.424 Z"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data="F1 M 235.016,188.341 L 235.016,835.919"/>
</Canvas>
</Canvas>
</Viewbox>

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View file

@ -0,0 +1,16 @@
<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">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ffaaaaaa" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ffaaaaaa" Data=" M 467.000,811.000 C 467.000,786.150 487.150,766.000 512.000,766.000 C 536.850,766.000 557.000,786.150 557.000,811.000 C 557.000,835.850 536.850,856.000 512.000,856.000 C 487.150,856.000 467.000,835.850 467.000,811.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="70.0" Stroke="#ffaaaaaa" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.220 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="70.0" Stroke="#ffaaaaaa" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.220 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
<Path StrokeThickness="70.0" Stroke="#ffaaaaaa" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
</Canvas>
</Canvas>
</Viewbox>

View file

@ -0,0 +1,16 @@
<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">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 467.000,811.000 C 467.000,786.150 487.150,766.000 512.000,766.000 C 536.850,766.000 557.000,786.150 557.000,811.000 C 557.000,835.850 536.850,856.000 512.000,856.000 C 487.150,856.000 467.000,835.850 467.000,811.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.221 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.221 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
</Canvas>
</Canvas>
</Viewbox>

View file

@ -0,0 +1,16 @@
<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">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 467.000,811.000 C 467.000,786.150 487.150,766.000 512.000,766.000 C 536.850,766.000 557.000,786.150 557.000,811.000 C 557.000,835.850 536.850,856.000 512.000,856.000 C 487.150,856.000 467.000,835.850 467.000,811.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="70.0" Stroke="#ffaaaaaa" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.220 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.220 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
<Path StrokeThickness="70.0" Stroke="#ffaaaaaa" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
</Canvas>
</Canvas>
</Viewbox>

View file

@ -0,0 +1,16 @@
<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">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 467.000,811.000 C 467.000,786.150 487.150,766.000 512.000,766.000 C 536.850,766.000 557.000,786.150 557.000,811.000 C 557.000,835.850 536.850,856.000 512.000,856.000 C 487.150,856.000 467.000,835.850 467.000,811.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="70.0" Stroke="#ffaaaaaa" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.220 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.220 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
</Canvas>
</Canvas>
</Viewbox>

View file

@ -0,0 +1,16 @@
<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">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ffdddddd" Data=" M 467.000,811.000 C 467.000,786.150 487.150,766.000 512.000,766.000 C 536.850,766.000 557.000,786.150 557.000,811.000 C 557.000,835.850 536.850,856.000 512.000,856.000 C 487.150,856.000 467.000,835.850 467.000,811.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="70.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="35.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.220 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="70.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.220 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="35.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
<Path StrokeThickness="70.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
</Canvas>
</Canvas>
</Viewbox>

View file

@ -0,0 +1,16 @@
<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">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 467.000,811.000 C 467.000,786.150 487.150,766.000 512.000,766.000 C 536.850,766.000 557.000,786.150 557.000,811.000 C 557.000,835.850 536.850,856.000 512.000,856.000 C 487.150,856.000 467.000,835.850 467.000,811.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.221 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.221 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
</Canvas>
</Canvas>
</Viewbox>

View file

@ -0,0 +1,16 @@
<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">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 467.000,811.000 C 467.000,786.150 487.150,766.000 512.000,766.000 C 536.850,766.000 557.000,786.150 557.000,811.000 C 557.000,835.850 536.850,856.000 512.000,856.000 C 487.150,856.000 467.000,835.850 467.000,811.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="70.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.220 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.220 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
<Path StrokeThickness="70.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
</Canvas>
</Canvas>
</Viewbox>

View file

@ -0,0 +1,16 @@
<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">
<Canvas>
<Path Data=" M 0.000,0.000 L 1024.000,0.000 L 1024.000,1024.000 L 0.000,1024.000 L 0.000,0.000 Z"/>
<Path StrokeThickness="1.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Fill="#ff000000" Data=" M 467.000,811.000 C 467.000,786.150 487.150,766.000 512.000,766.000 C 536.850,766.000 557.000,786.150 557.000,811.000 C 557.000,835.850 536.850,856.000 512.000,856.000 C 487.150,856.000 467.000,835.850 467.000,811.000 Z"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="70.0" Stroke="#ffdddddd" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 82.530,352.000 C 191.500,240.330 343.650,171.000 512.000,171.000 C 680.350,171.000 832.500,240.330 941.470,352.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.220 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 346.620,660.000 C 384.600,610.220 444.550,578.100 512.000,578.100 C 579.900,578.100 640.200,610.650 678.140,661.000"/>
<Path StrokeThickness="35.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
<Path StrokeThickness="70.0" Stroke="#ff000000" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeLineJoin="Round" Data=" M 213.050,505.000 C 288.650,426.810 394.650,378.200 512.000,378.200 C 629.350,378.200 735.350,426.810 810.950,505.000"/>
</Canvas>
</Canvas>
</Viewbox>

View file

@ -0,0 +1,12 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="40.000" Height="40.000">
<Canvas>
<Path Data="F1 M 32.000,36.000 L 9.000,36.000 L 9.000,5.000 L 32.000,5.000 L 32.000,36.000 Z"/>
</Canvas>
<Canvas>
<Path StrokeThickness="1.9" Stroke="#ff666666" StrokeLineJoin="Round" Data="F1 M 31.258,13.484 L 23.773,6.000 M 31.258,35.000 L 9.742,35.000 L 9.742,6.001 L 23.773,6.001 L 23.773,13.485 L 31.258,13.484 L 31.258,35.000 Z"/>
</Canvas>
</Canvas>
</Viewbox>

View file

@ -0,0 +1,12 @@
<Viewbox
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Canvas Width="40.000" Height="40.000">
<Canvas>
<Path Data="F1 M 32.000,36.000 L 9.000,36.000 L 9.000,5.000 L 32.000,5.000 L 32.000,36.000 Z"/>
</Canvas>
<Canvas>
<Path StrokeThickness="2.0" Stroke="#ff666666" StrokeLineJoin="Round" Data="F1 M 27.000,14.870 L 22.129,10.000 M 27.000,28.869 L 13.000,28.869 L 13.000,10.000 L 22.129,10.000 L 22.129,14.870 L 27.000,14.870 L 27.000,28.869 Z"/>
</Canvas>
</Canvas>
</Viewbox>

View file

@ -0,0 +1,21 @@
<Window x:Class="SafeExamBrowser.UserInterface.Mobile.LogWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile"
mc:Ignorable="d" Title="{Binding Path=WindowTitle}" Background="Black" Height="500" Width="1100" MinHeight="350" MinWidth="350"
WindowState="Maximized" WindowStartupLocation="CenterScreen" Icon="./Images/LogNotification.ico">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="./Templates/ScrollViewers.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<ScrollViewer x:Name="ScrollViewer" HorizontalScrollBarVisibility="Auto" Padding="5,5,5,0" VerticalScrollBarVisibility="Auto" Template="{StaticResource SmallBarScrollViewer}">
<TextBlock x:Name="LogContent" FontFamily="Consolas" />
</ScrollViewer>
</Grid>
</Window>

View file

@ -0,0 +1,97 @@
/*
* 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.ComponentModel;
using System.Windows;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface.Windows;
using SafeExamBrowser.Contracts.UserInterface.Windows.Events;
using SafeExamBrowser.UserInterface.Mobile.ViewModels;
namespace SafeExamBrowser.UserInterface.Mobile
{
public partial class LogWindow : Window, IWindow
{
private ILogger logger;
private LogViewModel model;
private WindowClosingEventHandler closing;
event WindowClosingEventHandler IWindow.Closing
{
add { closing += value; }
remove { closing -= value; }
}
public LogWindow(ILogger logger, IText text)
{
InitializeComponent();
this.logger = logger;
this.model = new LogViewModel(text, ScrollViewer, LogContent);
InitializeLogWindow();
}
public void BringToForeground()
{
Dispatcher.Invoke(() =>
{
if (WindowState == WindowState.Minimized)
{
WindowState = WindowState.Normal;
}
Activate();
});
}
public new void Close()
{
Dispatcher.Invoke(base.Close);
}
public new void Hide()
{
Dispatcher.Invoke(base.Hide);
}
public new void Show()
{
Dispatcher.Invoke(base.Show);
}
private void InitializeLogWindow()
{
DataContext = model;
Closing += LogWindow_Closing;
Loaded += LogWindow_Loaded;
}
private void LogWindow_Loaded(object sender, RoutedEventArgs e)
{
var log = logger.GetLog();
foreach (var content in log)
{
model.Notify(content);
}
logger.Subscribe(model);
logger.Debug("Opened log window.");
}
private void LogWindow_Closing(object sender, CancelEventArgs e)
{
logger.Unsubscribe(model);
logger.Debug("Closed log window.");
closing?.Invoke();
}
}
}

View file

@ -0,0 +1,90 @@
/*
* 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.Windows;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.UserInterface.MessageBox;
using SafeExamBrowser.Contracts.UserInterface.Windows;
using MessageBoxResult = SafeExamBrowser.Contracts.UserInterface.MessageBox.MessageBoxResult;
namespace SafeExamBrowser.UserInterface.Mobile
{
public class MessageBox : IMessageBox
{
private IText text;
public MessageBox(IText text)
{
this.text = text;
}
public MessageBoxResult Show(string message, string title, MessageBoxAction action = MessageBoxAction.Confirm, MessageBoxIcon icon = MessageBoxIcon.Information, IWindow parent = null)
{
var result = default(System.Windows.MessageBoxResult);
if (parent is Window window)
{
result = window.Dispatcher.Invoke(() => System.Windows.MessageBox.Show(window, message, title, ToButton(action), ToImage(icon)));
}
else
{
result = System.Windows.MessageBox.Show(message, title, ToButton(action), ToImage(icon));
}
return ToResult(result);
}
public MessageBoxResult Show(TextKey message, TextKey title, MessageBoxAction action = MessageBoxAction.Confirm, MessageBoxIcon icon = MessageBoxIcon.Information, IWindow parent = null)
{
return Show(text.Get(message), text.Get(title), action, icon, parent);
}
private MessageBoxButton ToButton(MessageBoxAction action)
{
switch (action)
{
case MessageBoxAction.YesNo:
return MessageBoxButton.YesNo;
default:
return MessageBoxButton.OK;
}
}
private MessageBoxImage ToImage(MessageBoxIcon icon)
{
switch (icon)
{
case MessageBoxIcon.Error:
return MessageBoxImage.Error;
case MessageBoxIcon.Question:
return MessageBoxImage.Question;
case MessageBoxIcon.Warning:
return MessageBoxImage.Warning;
default:
return MessageBoxImage.Information;
}
}
private MessageBoxResult ToResult(System.Windows.MessageBoxResult result)
{
switch (result)
{
case System.Windows.MessageBoxResult.Cancel:
return MessageBoxResult.Cancel;
case System.Windows.MessageBoxResult.No:
return MessageBoxResult.No;
case System.Windows.MessageBoxResult.OK:
return MessageBoxResult.Ok;
case System.Windows.MessageBoxResult.Yes:
return MessageBoxResult.Yes;
default:
return MessageBoxResult.None;
}
}
}
}

View file

@ -0,0 +1,45 @@
<Window x:Class="SafeExamBrowser.UserInterface.Mobile.PasswordDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:fa="http://schemas.fontawesome.io/icons/"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile"
mc:Ignorable="d" Height="200" Width="450" ResizeMode="NoResize" Topmost="True">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="./Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid FocusManager.FocusedElement="{Binding ElementName=Password}">
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<fa:ImageAwesome Grid.Column="0" x:Name="NoAdapterIcon" Foreground="LightGray" Icon="Key" Margin="25" Rotation="90" Width="50" />
<Grid Grid.Column="1" Margin="0,0,25,25">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" x:Name="Message" Margin="0,0,0,5" TextWrapping="WrapWithOverflow" VerticalAlignment="Bottom" />
<PasswordBox Grid.Row="1" x:Name="Password" Height="25" VerticalContentAlignment="Center" />
</Grid>
</Grid>
</Grid>
<Grid Grid.Row="1" Background="{StaticResource BackgroundBrush}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<WrapPanel Orientation="Horizontal" Margin="25,0" HorizontalAlignment="Right" VerticalAlignment="Center">
<Button x:Name="ConfirmButton" Cursor="Hand" Margin="10,0" Padding="10,5" MinWidth="75" />
<Button x:Name="CancelButton" Cursor="Hand" Padding="10,5" MinWidth="75" />
</WrapPanel>
</Grid>
</Grid>
</Window>

View file

@ -0,0 +1,106 @@
/*
* 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.Windows;
using System.Windows.Input;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.UserInterface.Windows;
using SafeExamBrowser.Contracts.UserInterface.Windows.Events;
namespace SafeExamBrowser.UserInterface.Mobile
{
public partial class PasswordDialog : Window, IPasswordDialog
{
private IText text;
private WindowClosingEventHandler closing;
event WindowClosingEventHandler IWindow.Closing
{
add { closing += value; }
remove { closing -= value; }
}
public PasswordDialog(string message, string title, IText text)
{
this.text = text;
InitializeComponent();
InitializePasswordDialog(message, title);
}
public void BringToForeground()
{
Dispatcher.Invoke(Activate);
}
public IPasswordDialogResult Show(IWindow parent = null)
{
return Dispatcher.Invoke(() =>
{
var result = new PasswordDialogResult { Success = false };
if (parent is Window)
{
Owner = parent as Window;
WindowStartupLocation = WindowStartupLocation.CenterOwner;
}
if (ShowDialog() is true)
{
result.Password = Password.Password;
result.Success = true;
}
return result;
});
}
private void InitializePasswordDialog(string message, string title)
{
Message.Text = message;
Title = title;
WindowStartupLocation = WindowStartupLocation.CenterScreen;
CancelButton.Content = text.Get(TextKey.PasswordDialog_Cancel);
CancelButton.Click += CancelButton_Click;
ConfirmButton.Content = text.Get(TextKey.PasswordDialog_Confirm);
ConfirmButton.Click += ConfirmButton_Click;
Closing += (o, args) => closing?.Invoke();
Password.KeyUp += Password_KeyUp;
}
private void CancelButton_Click(object sender, RoutedEventArgs e)
{
DialogResult = false;
Close();
}
private void ConfirmButton_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
Close();
}
private void Password_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
DialogResult = true;
Close();
}
}
private class PasswordDialogResult : IPasswordDialogResult
{
public string Password { get; set; }
public bool Success { get; set; }
}
}
}

View file

@ -0,0 +1,55 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("SafeExamBrowser.UserInterface.Mobile")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SafeExamBrowser.UserInterface.Mobile")]
[assembly: AssemblyCopyright("Copyright © 2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
//In order to begin building localizable applications, set
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> to en-US. Then uncomment
//the NeutralResourceLanguage attribute below. Update the "en-US" in
//the line below to match the UICulture setting in the project file.
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly:ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -0,0 +1,64 @@
<Window x:Class="SafeExamBrowser.UserInterface.Mobile.RuntimeWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile"
xmlns:s="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d" Background="White" Foreground="Black" Height="500" Width="850" WindowStyle="None" WindowStartupLocation="CenterScreen"
Icon="./Images/SafeExamBrowser.ico" ResizeMode="NoResize" Title="Safe Exam Browser" Topmost="True">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="./Templates/ScrollViewers.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Border x:Name="AnimatedBorder" Panel.ZIndex="10" BorderBrush="DodgerBlue" BorderThickness="5" Visibility="{Binding AnimatedBorderVisibility}">
<Border.Effect>
<BlurEffect Radius="10" />
</Border.Effect>
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<EventTrigger RoutedEvent="FrameworkElement.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimation AutoReverse="True" Storyboard.TargetProperty="Opacity" From="0.2" To="1.0" Duration="0:0:2.5" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
<Border BorderBrush="DodgerBlue" BorderThickness="1">
<Grid Margin="20">
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="25" />
<RowDefinition Height="3*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="400" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Grid.ColumnSpan="2" Margin="-25,0,0,0" Source="pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/SplashScreen.png" />
<TextBlock x:Name="InfoTextBlock" Grid.Column="1" Foreground="Gray" Margin="10,75,225,10" TextWrapping="Wrap" />
</Grid>
<ProgressBar x:Name="ProgressBar" Grid.Row="1" Background="WhiteSmoke" BorderThickness="0" Foreground="DodgerBlue"
IsIndeterminate="{Binding IsIndeterminate}" Maximum="{Binding MaxProgress}" Value="{Binding CurrentProgress}"
Visibility="{Binding ProgressBarVisibility}" />
<TextBlock x:Name="StatusTextBlock" Grid.Row="1" FontSize="12" FontWeight="Bold" HorizontalAlignment="Center"
Text="{Binding Status}" VerticalAlignment="Center" />
<Border Grid.Row="2" BorderBrush="DodgerBlue" BorderThickness="0,0.5,0,0">
<ScrollViewer x:Name="LogScrollViewer" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Margin="0,10,0,0" Template="{StaticResource SmallBarScrollViewer}">
<TextBlock x:Name="LogTextBlock" Background="Transparent" FontFamily="Consolas" FontSize="10" />
</ScrollViewer>
</Border>
</Grid>
</Border>
</Grid>
</Window>

View file

@ -0,0 +1,148 @@
/*
* 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.Windows;
using System.Windows.Documents;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface.Windows;
using SafeExamBrowser.Contracts.UserInterface.Windows.Events;
using SafeExamBrowser.UserInterface.Mobile.ViewModels;
namespace SafeExamBrowser.UserInterface.Mobile
{
public partial class RuntimeWindow : Window, IRuntimeWindow
{
private bool allowClose;
private AppConfig appConfig;
private IText text;
private RuntimeWindowViewModel model;
private WindowClosingEventHandler closing;
public bool TopMost
{
get { return Dispatcher.Invoke(() => Topmost); }
set { Dispatcher.Invoke(() => Topmost = value); }
}
event WindowClosingEventHandler IWindow.Closing
{
add { closing += value; }
remove { closing -= value; }
}
public RuntimeWindow(AppConfig appConfig, IText text)
{
this.appConfig = appConfig;
this.text = text;
InitializeComponent();
InitializeRuntimeWindow();
}
public void BringToForeground()
{
Dispatcher.Invoke(Activate);
}
public new void Close()
{
Dispatcher.Invoke(() =>
{
allowClose = true;
model.BusyIndication = false;
base.Close();
});
}
public new void Hide()
{
Dispatcher.Invoke(base.Hide);
}
public void HideProgressBar()
{
model.AnimatedBorderVisibility = Visibility.Visible;
model.ProgressBarVisibility = Visibility.Hidden;
}
public void Notify(ILogContent content)
{
Dispatcher.Invoke(() =>
{
model.Notify(content);
LogScrollViewer.ScrollToEnd();
});
}
public void Progress()
{
model.CurrentProgress += 1;
}
public void Regress()
{
model.CurrentProgress -= 1;
}
public void SetIndeterminate()
{
model.IsIndeterminate = true;
}
public void SetMaxValue(int max)
{
model.MaxProgress = max;
}
public void SetValue(int value)
{
model.CurrentProgress = value;
}
public void ShowProgressBar()
{
model.AnimatedBorderVisibility = Visibility.Hidden;
model.ProgressBarVisibility = Visibility.Visible;
}
public void UpdateStatus(TextKey key, bool busyIndication = false)
{
model.Status = text.Get(key);
model.BusyIndication = busyIndication;
}
public new void Show()
{
Dispatcher.Invoke(base.Show);
}
private void InitializeRuntimeWindow()
{
Title = $"{appConfig.ProgramTitle} - Version {appConfig.ProgramVersion}";
InfoTextBlock.Inlines.Add(new Run($"Version {appConfig.ProgramVersion}") { FontStyle = FontStyles.Italic });
InfoTextBlock.Inlines.Add(new LineBreak());
InfoTextBlock.Inlines.Add(new LineBreak());
InfoTextBlock.Inlines.Add(new Run(appConfig.ProgramCopyright) { FontSize = 10 });
model = new RuntimeWindowViewModel(LogTextBlock);
AnimatedBorder.DataContext = model;
ProgressBar.DataContext = model;
StatusTextBlock.DataContext = model;
Closing += (o, args) => args.Cancel = !allowClose;
#if DEBUG
Topmost = false;
#endif
}
}
}

View file

@ -0,0 +1,387 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{89BC24DD-FF31-496E-9816-A160B686A3D4}</ProjectGuid>
<OutputType>library</OutputType>
<RootNamespace>SafeExamBrowser.UserInterface.Mobile</RootNamespace>
<AssemblyName>SafeExamBrowser.UserInterface.Mobile</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="FontAwesome.WPF, Version=4.7.0.37774, Culture=neutral, PublicKeyToken=0758b07a11a4f466, processorArchitecture=MSIL">
<HintPath>..\packages\FontAwesome.WPF.4.7.0.9\lib\net40\FontAwesome.WPF.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="WindowsFormsIntegration" />
</ItemGroup>
<ItemGroup>
<Compile Include="AboutWindow.xaml.cs">
<DependentUpon>AboutWindow.xaml</DependentUpon>
</Compile>
<Compile Include="ActionCenter.xaml.cs">
<DependentUpon>ActionCenter.xaml</DependentUpon>
</Compile>
<Compile Include="BrowserWindow.xaml.cs">
<DependentUpon>BrowserWindow.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenterApplicationButton.xaml.cs">
<DependentUpon>ActionCenterApplicationButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenterApplicationControl.xaml.cs">
<DependentUpon>ActionCenterApplicationControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenterClock.xaml.cs">
<DependentUpon>ActionCenterClock.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>
<Compile Include="Controls\ActionCenterPowerSupplyControl.xaml.cs">
<DependentUpon>ActionCenterPowerSupplyControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenterQuitButton.xaml.cs">
<DependentUpon>ActionCenterQuitButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenterWirelessNetworkButton.xaml.cs">
<DependentUpon>ActionCenterWirelessNetworkButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\ActionCenterWirelessNetworkControl.xaml.cs">
<DependentUpon>ActionCenterWirelessNetworkControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\TaskbarApplicationControl.xaml.cs">
<DependentUpon>TaskbarApplicationControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\TaskbarApplicationInstanceButton.xaml.cs">
<DependentUpon>TaskbarApplicationInstanceButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\TaskbarClock.xaml.cs">
<DependentUpon>TaskbarClock.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\TaskbarKeyboardLayoutButton.xaml.cs">
<DependentUpon>TaskbarKeyboardLayoutButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\TaskbarKeyboardLayoutControl.xaml.cs">
<DependentUpon>TaskbarKeyboardLayoutControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\TaskbarNotificationButton.xaml.cs">
<DependentUpon>TaskbarNotificationButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\TaskbarPowerSupplyControl.xaml.cs">
<DependentUpon>TaskbarPowerSupplyControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\TaskbarQuitButton.xaml.cs">
<DependentUpon>TaskbarQuitButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\TaskbarWirelessNetworkButton.xaml.cs">
<DependentUpon>TaskbarWirelessNetworkButton.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\TaskbarWirelessNetworkControl.xaml.cs">
<DependentUpon>TaskbarWirelessNetworkControl.xaml</DependentUpon>
</Compile>
<Compile Include="LogWindow.xaml.cs">
<DependentUpon>LogWindow.xaml</DependentUpon>
</Compile>
<Compile Include="MessageBox.cs" />
<Compile Include="PasswordDialog.xaml.cs">
<DependentUpon>PasswordDialog.xaml</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="RuntimeWindow.xaml.cs">
<DependentUpon>RuntimeWindow.xaml</DependentUpon>
</Compile>
<Compile Include="SplashScreen.xaml.cs">
<DependentUpon>SplashScreen.xaml</DependentUpon>
</Compile>
<Compile Include="Taskbar.xaml.cs">
<DependentUpon>Taskbar.xaml</DependentUpon>
</Compile>
<Compile Include="UserInterfaceFactory.cs" />
<Compile Include="Utilities\IconResourceLoader.cs" />
<Compile Include="Utilities\VisualExtensions.cs" />
<Compile Include="Utilities\XamlIconResource.cs" />
<Compile Include="ViewModels\DateTimeViewModel.cs" />
<Compile Include="ViewModels\LogViewModel.cs" />
<Compile Include="ViewModels\ProgressIndicatorViewModel.cs" />
<Compile Include="ViewModels\RuntimeWindowViewModel.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SafeExamBrowser.Contracts\SafeExamBrowser.Contracts.csproj">
<Project>{47da5933-bef8-4729-94e6-abde2db12262}</Project>
<Name>SafeExamBrowser.Contracts</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Page Include="AboutWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="ActionCenter.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="BrowserWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\ActionCenterApplicationButton.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\ActionCenterApplicationControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Controls\ActionCenterClock.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\ActionCenterKeyboardLayoutButton.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\ActionCenterKeyboardLayoutControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\ActionCenterNotificationButton.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\ActionCenterPowerSupplyControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\ActionCenterQuitButton.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\ActionCenterWirelessNetworkButton.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\ActionCenterWirelessNetworkControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\TaskbarApplicationControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\TaskbarApplicationInstanceButton.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\TaskbarClock.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\TaskbarKeyboardLayoutButton.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\TaskbarKeyboardLayoutControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\TaskbarNotificationButton.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\TaskbarPowerSupplyControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\TaskbarQuitButton.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\TaskbarWirelessNetworkButton.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Controls\TaskbarWirelessNetworkControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Images\ZoomPageOut.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Images\ZoomPageIn.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Images\WiFi_Light_66.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Images\WiFi_Light_33.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Images\WiFi_Light_100.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Images\WiFi_Light_0.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Images\WiFi_66.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Images\WiFi_33.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Images\WiFi_100.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Images\WiFi_0.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Images\SkipBack.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Images\ShutDown.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Images\Reload.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Images\NavigateForward.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Images\NavigateBack.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Images\Menu.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Images\Keyboard.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Images\Battery.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Images\AboutNotification.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="LogWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="PasswordDialog.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="RuntimeWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="SplashScreen.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Taskbar.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Templates\Buttons.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Templates\Colors.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Templates\ScrollViewers.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Resource Include="Images\LogNotification.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Images\SafeExamBrowser.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Images\SplashScreen.png" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -0,0 +1,28 @@
<Window x:Class="SafeExamBrowser.UserInterface.Mobile.SplashScreen"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile"
mc:Ignorable="d"
Title="SplashScreen" Background="White" Height="200" Width="350" WindowStyle="None" WindowStartupLocation="CenterScreen"
Cursor="Wait" Icon="./Images/SafeExamBrowser.ico" ResizeMode="NoResize" Topmost="True">
<Border BorderBrush="DodgerBlue" BorderThickness="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="25" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="155" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Grid.ColumnSpan="2" Source="pack://application:,,,/SafeExamBrowser.UserInterface.Mobile;component/Images/SplashScreen.png" />
<TextBlock x:Name="InfoTextBlock" Grid.Column="1" Foreground="Gray" Margin="10,75,10,10" TextWrapping="Wrap" />
</Grid>
<ProgressBar x:Name="ProgressBar" Grid.Row="1" Minimum="0" Maximum="{Binding Path=MaxProgress}" Value="{Binding Path=CurrentProgress}" IsIndeterminate="{Binding Path=IsIndeterminate}" BorderThickness="0" />
<TextBlock x:Name="StatusTextBlock" Grid.Row="1" Text="{Binding Path=Status}" FontSize="12" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Border>
</Window>

View file

@ -0,0 +1,132 @@
/*
* 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.Windows;
using System.Windows.Documents;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.UserInterface.Windows;
using SafeExamBrowser.Contracts.UserInterface.Windows.Events;
using SafeExamBrowser.UserInterface.Mobile.ViewModels;
namespace SafeExamBrowser.UserInterface.Mobile
{
public partial class SplashScreen : Window, ISplashScreen
{
private bool allowClose;
private ProgressIndicatorViewModel model = new ProgressIndicatorViewModel();
private AppConfig appConfig;
private IText text;
private WindowClosingEventHandler closing;
public AppConfig AppConfig
{
set
{
Dispatcher.Invoke(() =>
{
appConfig = value;
UpdateAppInfo();
});
}
}
event WindowClosingEventHandler IWindow.Closing
{
add { closing += value; }
remove { closing -= value; }
}
public SplashScreen(IText text, AppConfig appConfig = null)
{
this.appConfig = appConfig;
this.text = text;
InitializeComponent();
InitializeSplashScreen();
}
public void BringToForeground()
{
Dispatcher.Invoke(Activate);
}
public new void Close()
{
Dispatcher.Invoke(() =>
{
allowClose = true;
model.BusyIndication = false;
base.Close();
});
}
public new void Hide()
{
Dispatcher.Invoke(base.Hide);
}
public new void Show()
{
Dispatcher.Invoke(base.Show);
}
public void Progress()
{
model.CurrentProgress += 1;
}
public void Regress()
{
model.CurrentProgress -= 1;
}
public void SetIndeterminate()
{
model.IsIndeterminate = true;
}
public void SetMaxValue(int max)
{
model.MaxProgress = max;
}
public void SetValue(int value)
{
model.CurrentProgress = value;
}
public void UpdateStatus(TextKey key, bool busyIndication = false)
{
model.Status = text.Get(key);
model.BusyIndication = busyIndication;
}
private void InitializeSplashScreen()
{
UpdateAppInfo();
StatusTextBlock.DataContext = model;
ProgressBar.DataContext = model;
Closing += (o, args) => args.Cancel = !allowClose;
}
private void UpdateAppInfo()
{
if (appConfig != null)
{
InfoTextBlock.Inlines.Add(new Run($"Version {appConfig.ProgramVersion}") { FontStyle = FontStyles.Italic });
InfoTextBlock.Inlines.Add(new LineBreak());
InfoTextBlock.Inlines.Add(new LineBreak());
InfoTextBlock.Inlines.Add(new Run(appConfig.ProgramCopyright) { FontSize = 10 });
}
}
}
}

View file

@ -0,0 +1,33 @@
<Window x:Class="SafeExamBrowser.UserInterface.Mobile.Taskbar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile.Controls"
xmlns:s="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d" Title="Taskbar" Background="#FFF0F0F0" Height="60" FontSize="16" Width="750" WindowStyle="None" Topmost="True"
ResizeMode="NoResize" Icon="./Images/SafeExamBrowser.ico">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="./Templates/ScrollViewers.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="60" />
</Grid.ColumnDefinitions>
<ScrollViewer Grid.Column="0" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled" Template="{StaticResource SmallBarScrollViewer}">
<StackPanel x:Name="ApplicationStackPanel" Orientation="Horizontal" />
</ScrollViewer>
<StackPanel Grid.Column="1" x:Name="NotificationStackPanel" Orientation="Horizontal" VerticalAlignment="Stretch" />
<StackPanel Grid.Column="2" x:Name="SystemControlStackPanel" Orientation="Horizontal" VerticalAlignment="Stretch" />
<local:TaskbarClock Grid.Column="3" x:Name="Clock" Foreground="DimGray" Padding="10,0,10,0" />
<local:TaskbarQuitButton Grid.Column="4" x:Name="QuitButton" />
</Grid>
</Window>

View file

@ -0,0 +1,151 @@
/*
* 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.ComponentModel;
using System.Windows;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Shell.Events;
using SafeExamBrowser.UserInterface.Mobile.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile
{
public partial class Taskbar : Window, ITaskbar
{
private bool allowClose;
private ILogger logger;
public bool ShowClock
{
set { Dispatcher.Invoke(() => Clock.Visibility = value ? Visibility.Visible : Visibility.Collapsed); }
}
public event QuitButtonClickedEventHandler QuitButtonClicked;
public Taskbar(ILogger logger)
{
this.logger = logger;
InitializeComponent();
InitializeTaskbar();
}
public void AddApplicationControl(IApplicationControl control)
{
if (control is UIElement uiElement)
{
ApplicationStackPanel.Children.Add(uiElement);
}
}
public void AddNotificationControl(INotificationControl control)
{
if (control is UIElement uiElement)
{
NotificationStackPanel.Children.Add(uiElement);
}
}
public void AddSystemControl(ISystemControl control)
{
if (control is UIElement uiElement)
{
SystemControlStackPanel.Children.Add(uiElement);
}
}
public new void Close()
{
Dispatcher.Invoke(base.Close);
}
public int GetAbsoluteHeight()
{
return Dispatcher.Invoke(() =>
{
var height = (int) this.TransformToPhysical(Width, Height).Y;
logger.Debug($"Calculated physical taskbar height is {height}px.");
return height;
});
}
public int GetRelativeHeight()
{
return Dispatcher.Invoke(() =>
{
var height = (int) Height;
logger.Debug($"Logical taskbar height is {height}px.");
return height;
});
}
public void InitializeBounds()
{
Dispatcher.Invoke(() =>
{
Width = SystemParameters.PrimaryScreenWidth;
Left = 0;
Top = SystemParameters.PrimaryScreenHeight - Height;
var position = this.TransformToPhysical(Left, Top);
var size = this.TransformToPhysical(Width, Height);
logger.Debug($"Set taskbar bounds to {Width}x{Height} at ({Left}/{Top}), in physical pixels: {size.X}x{size.Y} at ({position.X}/{position.Y}).");
});
}
public void InitializeText(IText text)
{
Dispatcher.Invoke(() =>
{
QuitButton.ToolTip = text.Get(TextKey.Shell_QuitButton);
});
}
public new void Show()
{
Dispatcher.Invoke(base.Show);
}
private void QuitButton_Clicked(CancelEventArgs args)
{
QuitButtonClicked?.Invoke(args);
allowClose = !args.Cancel;
}
private void Taskbar_Closing(object sender, CancelEventArgs e)
{
if (!allowClose)
{
e.Cancel = true;
return;
}
foreach (var child in SystemControlStackPanel.Children)
{
if (child is ISystemControl systemControl)
{
systemControl.Close();
}
}
}
private void InitializeTaskbar()
{
Closing += Taskbar_Closing;
Loaded += (o, args) => InitializeBounds();
QuitButton.Clicked += QuitButton_Clicked;
}
}
}

View file

@ -0,0 +1,61 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
<ControlTemplate x:Key="ActionCenterButton" TargetType="Button">
<Border x:Name="ButtonContent" Background="Transparent" BorderBrush="Transparent" BorderThickness="1" Cursor="Hand" Padding="{TemplateBinding Padding}">
<ContentPresenter ContentSource="Content" HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
RenderOptions.BitmapScalingMode="HighQuality" VerticalAlignment="{TemplateBinding VerticalAlignment}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ButtonContent" Property="Background" Value="#AAADD8E6" />
<Setter TargetName="ButtonContent" Property="BorderBrush" Value="DodgerBlue" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="ButtonContent" Property="BorderBrush" Value="#AA87CEEB" />
<Setter TargetName="ButtonContent" Property="BorderThickness" Value="2" />
<Setter Property="Cursor" Value="Hand" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate x:Key="BrowserButton" TargetType="Button">
<Border x:Name="ButtonContent" Background="Transparent" BorderBrush="Transparent" BorderThickness="1" Cursor="Hand" Padding="{TemplateBinding Padding}">
<ContentPresenter ContentSource="Content" HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
RenderOptions.BitmapScalingMode="HighQuality" VerticalAlignment="{TemplateBinding VerticalAlignment}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="ButtonContent" Property="Opacity" Value="0.25" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ButtonContent" Property="Background" Value="LightBlue" />
<Setter TargetName="ButtonContent" Property="BorderBrush" Value="DodgerBlue" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="ButtonContent" Property="BorderBrush" Value="SkyBlue" />
<Setter TargetName="ButtonContent" Property="BorderThickness" Value="2" />
<Setter Property="Cursor" Value="Hand" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate x:Key="TaskbarButton" TargetType="Button">
<Border x:Name="ButtonContent" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding Background}"
BorderThickness="1" Cursor="Hand" Padding="{TemplateBinding Padding}">
<ContentPresenter ContentSource="Content" HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
RenderOptions.BitmapScalingMode="HighQuality" VerticalAlignment="{TemplateBinding VerticalAlignment}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ButtonContent" Property="Background" Value="LightBlue" />
<Setter TargetName="ButtonContent" Property="BorderBrush" Value="DodgerBlue" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="ButtonContent" Property="BorderBrush" Value="SkyBlue" />
<Setter TargetName="ButtonContent" Property="BorderThickness" Value="2" />
<Setter Property="Cursor" Value="Hand" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ResourceDictionary>

View file

@ -0,0 +1,5 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="BackgroundBrush">#FFF0F0F0</SolidColorBrush>
<SolidColorBrush x:Key="ActionCenterDarkBrush">#AA808080</SolidColorBrush>
</ResourceDictionary>

View file

@ -0,0 +1,59 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ControlTemplate x:Key="Thumb" TargetType="{x:Type Thumb}">
<Rectangle x:Name="Rectangle" Fill="DarkGray" Height="Auto" Width="Auto" />
<ControlTemplate.Triggers>
<Trigger Property="IsDragging" Value="True">
<Setter TargetName="Rectangle" Property="Fill" Value="Black" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<ControlTemplate x:Key="RepeatButton" TargetType="RepeatButton">
<Rectangle Fill="DarkGray" Height="Auto" Width="Auto" />
</ControlTemplate>
<ControlTemplate x:Key="VerticalScrollBar" TargetType="{x:Type ScrollBar}">
<Track Name="PART_Track" Height="Auto" IsDirectionReversed="True" Width="10">
<Track.DecreaseRepeatButton>
<RepeatButton Margin="4.5,0" Template="{StaticResource RepeatButton}" />
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb Cursor="ScrollNS" Focusable="False" Margin="2.5,0" OverridesDefaultStyle="True" SnapsToDevicePixels="True" Template="{StaticResource Thumb}" />
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton Margin="4.5,0" Template="{StaticResource RepeatButton}" />
</Track.IncreaseRepeatButton>
</Track>
</ControlTemplate>
<ControlTemplate x:Key="HorizontalScrollBar" TargetType="{x:Type ScrollBar}">
<Track Name="PART_Track" Height="10" IsDirectionReversed="False" Width="Auto">
<Track.DecreaseRepeatButton>
<RepeatButton Margin="0,4.5" Template="{StaticResource RepeatButton}" />
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb Cursor="ScrollWE" Focusable="False" Margin="0,2.5" OverridesDefaultStyle="True" SnapsToDevicePixels="True" Template="{StaticResource Thumb}" />
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton Margin="0,4.5" Template="{StaticResource RepeatButton}" />
</Track.IncreaseRepeatButton>
</Track>
</ControlTemplate>
<ControlTemplate x:Key="SmallBarScrollViewer" TargetType="{x:Type ScrollViewer}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ScrollContentPresenter Grid.Column="0" Grid.Row="0" />
<ScrollBar Name="PART_VerticalScrollBar" Grid.Column="1" Grid.Row="0" Background="LightGray" OverridesDefaultStyle="True"
Value="{TemplateBinding VerticalOffset}" Maximum="{TemplateBinding ScrollableHeight}" Template="{StaticResource VerticalScrollBar}"
ViewportSize="{TemplateBinding ViewportHeight}" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" />
<ScrollBar Name="PART_HorizontalScrollBar" Grid.Column="0" Grid.Row="1" Background="LightGray" OverridesDefaultStyle="True" Orientation="Horizontal"
Value="{TemplateBinding HorizontalOffset}" Maximum="{TemplateBinding ScrollableWidth}" Template="{StaticResource HorizontalScrollBar}"
ViewportSize="{TemplateBinding ViewportWidth}" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" />
</Grid>
</ControlTemplate>
</ResourceDictionary>

View file

@ -0,0 +1,179 @@
/*
* 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.Threading;
using System.Windows;
using System.Windows.Media;
using FontAwesome.WPF;
using SafeExamBrowser.Contracts.Applications;
using SafeExamBrowser.Contracts.Client;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.Browser;
using SafeExamBrowser.Contracts.UserInterface.Shell;
using SafeExamBrowser.Contracts.UserInterface.Windows;
using SafeExamBrowser.UserInterface.Mobile.Controls;
namespace SafeExamBrowser.UserInterface.Mobile
{
public class UserInterfaceFactory : IUserInterfaceFactory
{
private IText text;
public UserInterfaceFactory(IText text)
{
this.text = text;
InitializeFontAwesome();
}
public IWindow CreateAboutWindow(AppConfig appConfig)
{
return new AboutWindow(appConfig, text);
}
public IApplicationControl CreateApplicationControl(IApplicationInfo info, Location location)
{
if (location == Location.ActionCenter)
{
return new ActionCenterApplicationControl(info);
}
else
{
return new TaskbarApplicationControl(info);
}
}
public IBrowserWindow CreateBrowserWindow(IBrowserControl control, BrowserSettings settings, bool isMainWindow)
{
return new BrowserWindow(control, settings, isMainWindow, text);
}
public ISystemKeyboardLayoutControl CreateKeyboardLayoutControl(Location location)
{
if (location == Location.ActionCenter)
{
return new ActionCenterKeyboardLayoutControl();
}
else
{
return new TaskbarKeyboardLayoutControl();
}
}
public IWindow CreateLogWindow(ILogger logger)
{
LogWindow logWindow = null;
var logWindowReadyEvent = new AutoResetEvent(false);
var logWindowThread = new Thread(() =>
{
logWindow = new LogWindow(logger, text);
logWindow.Closed += (o, args) => logWindow.Dispatcher.InvokeShutdown();
logWindow.Show();
logWindowReadyEvent.Set();
System.Windows.Threading.Dispatcher.Run();
});
logWindowThread.SetApartmentState(ApartmentState.STA);
logWindowThread.IsBackground = true;
logWindowThread.Start();
logWindowReadyEvent.WaitOne();
return logWindow;
}
public INotificationControl CreateNotificationControl(INotificationInfo info, Location location)
{
if (location == Location.ActionCenter)
{
return new ActionCenterNotificationButton(info);
}
else
{
return new TaskbarNotificationButton(info);
}
}
public IPasswordDialog CreatePasswordDialog(string message, string title)
{
return Application.Current.Dispatcher.Invoke(() => new PasswordDialog(message, title, text));
}
public IPasswordDialog CreatePasswordDialog(TextKey message, TextKey title)
{
return Application.Current.Dispatcher.Invoke(() => new PasswordDialog(text.Get(message), text.Get(title), text));
}
public ISystemPowerSupplyControl CreatePowerSupplyControl(Location location)
{
if (location == Location.ActionCenter)
{
return new ActionCenterPowerSupplyControl();
}
else
{
return new TaskbarPowerSupplyControl();
}
}
public IRuntimeWindow CreateRuntimeWindow(AppConfig appConfig)
{
return Application.Current.Dispatcher.Invoke(() => new RuntimeWindow(appConfig, text));
}
public ISplashScreen CreateSplashScreen(AppConfig appConfig = null)
{
SplashScreen splashScreen = null;
var splashReadyEvent = new AutoResetEvent(false);
var splashScreenThread = new Thread(() =>
{
splashScreen = new SplashScreen(text, appConfig);
splashScreen.Closed += (o, args) => splashScreen.Dispatcher.InvokeShutdown();
splashScreen.Show();
splashReadyEvent.Set();
System.Windows.Threading.Dispatcher.Run();
});
splashScreenThread.SetApartmentState(ApartmentState.STA);
splashScreenThread.Name = nameof(SplashScreen);
splashScreenThread.IsBackground = true;
splashScreenThread.Start();
splashReadyEvent.WaitOne();
return splashScreen;
}
public ISystemWirelessNetworkControl CreateWirelessNetworkControl(Location location)
{
if (location == Location.ActionCenter)
{
return new ActionCenterWirelessNetworkControl();
}
else
{
return new TaskbarWirelessNetworkControl();
}
}
private void InitializeFontAwesome()
{
// To be able to use FontAwesome in XAML icon resources, we need to make sure that the FontAwesome.WPF assembly is loaded into
// the AppDomain before attempting to load an icon resource - thus the creation of an unused image below...
ImageAwesome.CreateImageSource(FontAwesomeIcon.FontAwesome, Brushes.Black);
}
}
}

View file

@ -0,0 +1,71 @@
/*
* 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 System.Windows.Documents;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using SafeExamBrowser.Contracts.Core;
namespace SafeExamBrowser.UserInterface.Mobile.Utilities
{
/// <summary>
/// TODO: Move to shared library?
/// </summary>
internal static class IconResourceLoader
{
internal static UIElement Load(IIconResource resource)
{
try
{
if (resource.IsBitmapResource)
{
return LoadBitmapResource(resource);
}
else if (resource.IsXamlResource)
{
return LoadXamlResource(resource);
}
}
catch (Exception)
{
return NotFoundSymbol();
}
throw new NotSupportedException($"Application icon resource of type '{resource.GetType()}' is not supported!");
}
private static UIElement LoadBitmapResource(IIconResource resource)
{
return new Image
{
Source = new BitmapImage(resource.Uri)
};
}
private static UIElement LoadXamlResource(IIconResource resource)
{
using (var stream = Application.GetResourceStream(resource.Uri)?.Stream)
{
return XamlReader.Load(stream) as UIElement;
}
}
private static UIElement NotFoundSymbol()
{
return new TextBlock(new Run("X") { Foreground = Brushes.Red, FontWeight = FontWeights.Bold })
{
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
};
}
}
}

View file

@ -0,0 +1,45 @@
/*
* 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.Windows;
using System.Windows.Interop;
using System.Windows.Media;
namespace SafeExamBrowser.UserInterface.Mobile.Utilities
{
/// <summary>
/// TODO: Move to shared library?
/// </summary>
internal static class VisualExtensions
{
/// <summary>
/// WPF works with device-independent pixels. This method is required to
/// transform such values to their absolute, device-specific pixel value.
/// Source: https://stackoverflow.com/questions/3286175/how-do-i-convert-a-wpf-size-to-physical-pixels
/// </summary>
internal static Vector TransformToPhysical(this Visual visual, double x, double y)
{
Matrix transformToDevice;
var source = PresentationSource.FromVisual(visual);
if (source != null)
{
transformToDevice = source.CompositionTarget.TransformToDevice;
}
else
{
using (var newSource = new HwndSource(new HwndSourceParameters()))
{
transformToDevice = newSource.CompositionTarget.TransformToDevice;
}
}
return transformToDevice.Transform(new Vector(x, y));
}
}
}

View file

@ -0,0 +1,28 @@
/*
* 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 SafeExamBrowser.Contracts.Core;
namespace SafeExamBrowser.UserInterface.Mobile.Utilities
{
/// <summary>
/// TODO: Move to shared library?
/// </summary>
internal class XamlIconResource : IIconResource
{
public Uri Uri { get; private set; }
public bool IsBitmapResource => false;
public bool IsXamlResource => true;
public XamlIconResource(Uri uri)
{
Uri = uri;
}
}
}

View file

@ -0,0 +1,47 @@
/*
* 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.ComponentModel;
using System.Timers;
namespace SafeExamBrowser.UserInterface.Mobile.ViewModels
{
internal class DateTimeViewModel : INotifyPropertyChanged
{
private Timer timer;
private readonly bool showSeconds;
public string Date { get; private set; }
public string Time { get; private set; }
public string ToolTip { get; private set; }
public event PropertyChangedEventHandler PropertyChanged;
public DateTimeViewModel(bool showSeconds)
{
this.showSeconds = showSeconds;
this.timer = new Timer(1000);
this.timer.Elapsed += Timer_Elapsed;
this.timer.Start();
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
var date = DateTime.Now;
Date = date.ToShortDateString();
Time = showSeconds ? date.ToLongTimeString() : date.ToShortTimeString();
ToolTip = $"{date.ToLongDateString()} {date.ToLongTimeString()}";
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Time)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Date)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ToolTip)));
}
}
}

Some files were not shown because too many files have changed in this diff Show more