SEBWIN-312: Implemented task view for mobile UI.

This commit is contained in:
dbuechel 2019-11-20 14:45:33 +01:00
parent fbe03b86ea
commit 5ccbd2aae4
12 changed files with 308 additions and 13 deletions

View file

@ -309,8 +309,7 @@ namespace SafeExamBrowser.Client
switch (uiMode)
{
case UserInterfaceMode.Mobile:
// TODO
throw new NotImplementedException();
return new Mobile.TaskView();
default:
return new Desktop.TaskView();
}

View file

@ -12,7 +12,7 @@
</ResourceDictionary>
</UserControl.Resources>
<Grid Margin="5" Height="150" Width="250">
<Border Name="Border" Background="{StaticResource BackgroundTransparentEmphasisBrush}" BorderBrush="DarkGray" BorderThickness="2" Visibility="Hidden" />
<Border Name="Indicator" Background="{StaticResource BackgroundTransparentEmphasisBrush}" BorderThickness="0" Visibility="Hidden" />
<StackPanel HorizontalAlignment="Center" Orientation="Vertical" VerticalAlignment="Center">
<ContentControl Name="Icon" MaxWidth="40" />
<TextBlock Name="Title" Margin="10" Foreground="{StaticResource PrimaryTextBrush}" TextTrimming="CharacterEllipsis" />

View file

@ -35,15 +35,15 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
internal void Deselect()
{
Border.Visibility = Visibility.Hidden;
Icon.MaxWidth = 40;
Indicator.Visibility = Visibility.Hidden;
Title.FontWeight = FontWeights.Normal;
}
internal void Select()
{
Border.Visibility = Visibility.Visible;
Icon.MaxWidth = 50;
Indicator.Visibility = Visibility.Visible;
Title.FontWeight = FontWeights.SemiBold;
}

View file

@ -4,11 +4,12 @@
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">
mc:Ignorable="d" Title="ActionCenter" Height="1000" Width="400" Background="{DynamicResource BackgroundTransparentBrush}"
AllowsTransparency="True" FontSize="16" WindowStyle="None" Topmost="True" ResizeMode="NoResize">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="./Templates/Colors.xaml" />
<ResourceDictionary Source="./Templates/ScrollViewers.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

View file

@ -20,8 +20,8 @@
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" x:Name="ApplicationName" Background="#99D3D3D3" FontWeight="Bold" Padding="5" TextAlignment="Center" />
<TextBlock Grid.Row="0" x:Name="ApplicationName" Background="{StaticResource BackgroundTransparentEmphasisBrush}" FontWeight="Bold" Padding="5" TextAlignment="Center" />
<ContentControl Grid.Row="1" x:Name="ApplicationButton" FontWeight="Bold" />
<StackPanel Grid.Row="2" x:Name="InstancePanel" Background="#99D3D3D3" Orientation="Vertical" />
<StackPanel Grid.Row="2" x:Name="InstancePanel" Background="{StaticResource BackgroundTransparentEmphasisBrush}" Orientation="Vertical" />
</Grid>
</UserControl>

View file

@ -0,0 +1,21 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.TaskViewInstanceControl"
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">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Margin="5" Height="150" Width="250">
<Border Name="Indicator" Background="{StaticResource BackgroundTransparentEmphasisBrush}" BorderThickness="0" Visibility="Hidden" />
<StackPanel HorizontalAlignment="Center" Orientation="Vertical" VerticalAlignment="Center">
<ContentControl Name="Icon" MaxWidth="40" />
<TextBlock Name="Title" Margin="10" Foreground="{StaticResource PrimaryTextBrush}" TextTrimming="CharacterEllipsis" />
</StackPanel>
</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.Applications.Contracts;
using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
public partial class TaskViewInstanceControl : UserControl
{
private IApplicationInstance instance;
internal InstanceIdentifier Id => instance.Id;
public TaskViewInstanceControl(IApplicationInstance instance)
{
this.instance = instance;
InitializeComponent();
InitializeControl();
}
internal void Activate()
{
instance.Activate();
}
internal void Deselect()
{
Icon.MaxWidth = 40;
Indicator.Visibility = Visibility.Hidden;
Title.FontWeight = FontWeights.Normal;
}
internal void Select()
{
Icon.MaxWidth = 50;
Indicator.Visibility = Visibility.Visible;
Title.FontWeight = FontWeights.SemiBold;
}
private void InitializeControl()
{
Icon.Content = IconResourceLoader.Load(instance.Icon);
Title.Text = instance.Name;
instance.IconChanged += Instance_IconChanged;
instance.NameChanged += Instance_NameChanged;
}
private void Instance_NameChanged(string name)
{
Dispatcher.InvokeAsync(() => Title.Text = name);
}
private void Instance_IconChanged(IconResource icon)
{
Dispatcher.InvokeAsync(() => Icon.Content = IconResourceLoader.Load(instance.Icon));
}
}
}

View file

@ -143,6 +143,9 @@
<Compile Include="Controls\TaskbarWirelessNetworkControl.xaml.cs">
<DependentUpon>TaskbarWirelessNetworkControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\TaskViewInstanceControl.xaml.cs">
<DependentUpon>TaskViewInstanceControl.xaml</DependentUpon>
</Compile>
<Compile Include="LockScreen.xaml.cs">
<DependentUpon>LockScreen.xaml</DependentUpon>
</Compile>
@ -168,6 +171,9 @@
<Compile Include="Taskbar.xaml.cs">
<DependentUpon>Taskbar.xaml</DependentUpon>
</Compile>
<Compile Include="TaskView.xaml.cs">
<DependentUpon>TaskView.xaml</DependentUpon>
</Compile>
<Compile Include="UserInterfaceFactory.cs" />
<Compile Include="ViewModels\DateTimeViewModel.cs" />
<Compile Include="ViewModels\LogViewModel.cs" />
@ -353,6 +359,10 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Page Include="Controls\TaskViewInstanceControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Images\ZoomPageOut.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
@ -457,6 +467,10 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="TaskView.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Templates\Buttons.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>

View file

@ -0,0 +1,19 @@
<Window x:Class="SafeExamBrowser.UserInterface.Mobile.TaskView"
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" AllowsTransparency="True" Background="{DynamicResource BackgroundTransparentBrush}" BorderBrush="DodgerBlue" BorderThickness="1"
Title="TaskView" Topmost="True" Height="450" SizeToContent="WidthAndHeight" Width="800" WindowStartupLocation="CenterScreen" WindowStyle="None">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="./Templates/Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<StackPanel Name="Rows" Margin="10" Orientation="Vertical" />
</Grid>
</Window>

View file

@ -0,0 +1,167 @@
/*
* 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.Linq;
using System.Windows;
using System.Windows.Controls;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Mobile.Controls;
namespace SafeExamBrowser.UserInterface.Mobile
{
public partial class TaskView : Window, ITaskView
{
private LinkedListNode<TaskViewInstanceControl> current;
private LinkedList<TaskViewInstanceControl> controls;
private List<IApplicationInstance> instances;
public TaskView()
{
controls = new LinkedList<TaskViewInstanceControl>();
instances = new List<IApplicationInstance>();
InitializeComponent();
}
public void Add(IApplication application)
{
application.InstanceStarted += Application_InstanceStarted;
}
public void Register(ITaskViewActivator activator)
{
activator.Deactivated += Activator_Deactivated;
activator.NextActivated += Activator_Next;
activator.PreviousActivated += Activator_Previous;
}
private void Application_InstanceStarted(IApplicationInstance instance)
{
Dispatcher.InvokeAsync(() => Add(instance));
}
private void Activator_Deactivated()
{
Dispatcher.InvokeAsync(ActivateAndHide);
}
private void Activator_Next()
{
Dispatcher.InvokeAsync(SelectNext);
}
private void Activator_Previous()
{
Dispatcher.InvokeAsync(SelectPrevious);
}
private void Instance_Terminated(InstanceIdentifier id)
{
Dispatcher.InvokeAsync(() => Remove(id));
}
private void ActivateAndHide()
{
Activate();
current?.Value.Activate();
Hide();
}
private void Add(IApplicationInstance instance)
{
instance.Terminated += Instance_Terminated;
instances.Add(instance);
Update();
}
private void Remove(InstanceIdentifier id)
{
var instance = instances.FirstOrDefault(i => i.Id == id);
if (instance != default(IApplicationInstance))
{
instances.Remove(instance);
Update();
}
}
private void SelectNext()
{
ShowConditional();
if (current != null)
{
current.Value.Deselect();
current = current.Next ?? controls.First;
current.Value.Select();
}
}
private void SelectPrevious()
{
ShowConditional();
if (current != null)
{
current.Value.Deselect();
current = current.Previous ?? controls.Last;
current.Value.Select();
}
}
private void ShowConditional()
{
if (instances.Any() && Visibility != Visibility.Visible)
{
Show();
Activate();
}
}
private void Update()
{
var max = Math.Ceiling(Math.Sqrt(instances.Count));
var stack = new Stack<IApplicationInstance>(instances);
controls.Clear();
Rows.Children.Clear();
for (var rowCount = 0; rowCount < max && stack.Any(); rowCount++)
{
var row = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Center };
Rows.Children.Add(row);
for (var columnIndex = 0; columnIndex < max && stack.Any(); columnIndex++)
{
var instance = stack.Pop();
var control = new TaskViewInstanceControl(instance);
controls.AddLast(control);
row.Children.Add(control);
}
}
current = controls.First;
current?.Value.Select();
UpdateLayout();
Left = (SystemParameters.WorkArea.Width - Width) / 2 + SystemParameters.WorkArea.Left;
Top = (SystemParameters.WorkArea.Height - Height) / 2 + SystemParameters.WorkArea.Top;
if (!instances.Any())
{
Hide();
}
}
}
}

View file

@ -5,11 +5,12 @@
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">
mc:Ignorable="d" Title="Taskbar" Background="{DynamicResource BackgroundBrush}" Height="60" FontSize="16" Width="750" WindowStyle="None"
Topmost="True" ResizeMode="NoResize" Icon="./Images/SafeExamBrowser.ico">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="./Templates/Colors.xaml" />
<ResourceDictionary Source="./Templates/ScrollViewers.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
@ -27,7 +28,7 @@
</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:TaskbarClock Grid.Column="3" x:Name="Clock" Foreground="{StaticResource SecondaryTextBrush}" Padding="10,0,10,0" />
<local:TaskbarQuitButton Grid.Column="4" x:Name="QuitButton" />
</Grid>
</Window>

View file

@ -1,5 +1,9 @@
<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>
<SolidColorBrush x:Key="BackgroundBrush">#FFF0F0F0</SolidColorBrush>
<SolidColorBrush x:Key="BackgroundTransparentBrush">#EEF0F0F0</SolidColorBrush>
<SolidColorBrush x:Key="BackgroundTransparentEmphasisBrush">#99D3D3D3</SolidColorBrush>
<SolidColorBrush x:Key="PrimaryTextBrush">Black</SolidColorBrush>
<SolidColorBrush x:Key="SecondaryTextBrush">DimGray</SolidColorBrush>
</ResourceDictionary>