SEBWIN-312: Finished first draft of task view.

This commit is contained in:
dbuechel 2019-11-20 10:45:08 +01:00
parent 08bf49b61b
commit fbe03b86ea
20 changed files with 227 additions and 85 deletions

View file

@ -21,13 +21,12 @@ namespace SafeExamBrowser.Applications
private const int ONE_SECOND = 1000; private const int ONE_SECOND = 1000;
private ILogger logger; private ILogger logger;
private string name;
private IProcess process; private IProcess process;
private Timer timer; private Timer timer;
public IconResource Icon { get; } public IconResource Icon { get; }
public InstanceIdentifier Id { get; } public InstanceIdentifier Id { get; }
public string Name { get; } public string Name { get; private set; }
public event IconChangedEventHandler IconChanged { add { } remove { } } public event IconChangedEventHandler IconChanged { add { } remove { } }
public event NameChangedEventHandler NameChanged; public event NameChangedEventHandler NameChanged;
@ -114,12 +113,12 @@ namespace SafeExamBrowser.Applications
private void Timer_Elapsed(object sender, ElapsedEventArgs e) private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{ {
var success = process.TryGetWindowTitle(out var title); var success = process.TryGetWindowTitle(out var title);
var hasChanged = name?.Equals(title, StringComparison.Ordinal) != true; var hasChanged = Name?.Equals(title, StringComparison.Ordinal) != true;
if (success && hasChanged) if (success && hasChanged)
{ {
name = title; Name = title;
NameChanged?.Invoke(name); NameChanged?.Invoke(Name);
} }
timer.Start(); timer.Start();

View file

@ -194,8 +194,9 @@ namespace SafeExamBrowser.Browser
private void Control_TitleChanged(string title) private void Control_TitleChanged(string title)
{ {
window.UpdateTitle(title); Name = title;
NameChanged?.Invoke(title); window.UpdateTitle(Name);
NameChanged?.Invoke(Name);
} }
private void DisplayHandler_FaviconChanged(string uri) private void DisplayHandler_FaviconChanged(string uri)

View file

@ -24,6 +24,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
private ClientContext context; private ClientContext context;
private Mock<ILogger> logger; private Mock<ILogger> logger;
private Mock<ITaskbar> taskbar; private Mock<ITaskbar> taskbar;
private Mock<ITaskView> taskView;
private Mock<IUserInterfaceFactory> uiFactory; private Mock<IUserInterfaceFactory> uiFactory;
private BrowserOperation sut; private BrowserOperation sut;
@ -36,11 +37,12 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
context = new ClientContext(); context = new ClientContext();
logger = new Mock<ILogger>(); logger = new Mock<ILogger>();
taskbar = new Mock<ITaskbar>(); taskbar = new Mock<ITaskbar>();
taskView = new Mock<ITaskView>();
uiFactory = new Mock<IUserInterfaceFactory>(); uiFactory = new Mock<IUserInterfaceFactory>();
context.Browser = browser.Object; context.Browser = browser.Object;
sut = new BrowserOperation(actionCenter.Object, context, logger.Object, taskbar.Object, uiFactory.Object); sut = new BrowserOperation(actionCenter.Object, context, logger.Object, taskbar.Object, taskView.Object, uiFactory.Object);
} }
[TestMethod] [TestMethod]
@ -59,5 +61,11 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
sut.Revert(); sut.Revert();
browser.Verify(c => c.Terminate(), Times.Once); browser.Verify(c => c.Terminate(), Times.Once);
} }
[TestMethod]
public void TODO()
{
// TODO: Test initialization of task view!
}
} }
} }

View file

@ -71,6 +71,7 @@ namespace SafeExamBrowser.Client
private IRuntimeProxy runtimeProxy; private IRuntimeProxy runtimeProxy;
private ISystemInfo systemInfo; private ISystemInfo systemInfo;
private ITaskbar taskbar; private ITaskbar taskbar;
private ITaskView taskView;
private IText text; private IText text;
private ITextResource textResource; private ITextResource textResource;
private IUserInterfaceFactory uiFactory; private IUserInterfaceFactory uiFactory;
@ -94,6 +95,7 @@ namespace SafeExamBrowser.Client
uiFactory = BuildUserInterfaceFactory(); uiFactory = BuildUserInterfaceFactory();
runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), ModuleLogger(nameof(RuntimeProxy)), Interlocutor.Client); runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), ModuleLogger(nameof(RuntimeProxy)), Interlocutor.Client);
taskbar = BuildTaskbar(); taskbar = BuildTaskbar();
taskView = BuildTaskView();
var processFactory = new ProcessFactory(ModuleLogger(nameof(ProcessFactory))); var processFactory = new ProcessFactory(ModuleLogger(nameof(ProcessFactory)));
var applicationFactory = new ApplicationFactory(ModuleLogger(nameof(ApplicationFactory)), processFactory); var applicationFactory = new ApplicationFactory(ModuleLogger(nameof(ApplicationFactory)), processFactory);
@ -197,7 +199,7 @@ namespace SafeExamBrowser.Client
{ {
var moduleLogger = ModuleLogger(nameof(BrowserApplication)); var moduleLogger = ModuleLogger(nameof(BrowserApplication));
var browser = new BrowserApplication(context.AppConfig, context.Settings.Browser, messageBox, moduleLogger, text, uiFactory); var browser = new BrowserApplication(context.AppConfig, context.Settings.Browser, messageBox, moduleLogger, text, uiFactory);
var operation = new BrowserOperation(actionCenter, context, logger, taskbar, uiFactory); var operation = new BrowserOperation(actionCenter, context, logger, taskbar, taskView, uiFactory);
context.Browser = browser; context.Browser = browser;
@ -242,7 +244,6 @@ namespace SafeExamBrowser.Client
var logInfo = new LogNotificationInfo(text); var logInfo = new LogNotificationInfo(text);
var logController = new LogNotificationController(logger, uiFactory); var logController = new LogNotificationController(logger, uiFactory);
var powerSupply = new PowerSupply(ModuleLogger(nameof(PowerSupply))); var powerSupply = new PowerSupply(ModuleLogger(nameof(PowerSupply)));
var taskView = BuildTaskView();
var wirelessAdapter = new WirelessAdapter(ModuleLogger(nameof(WirelessAdapter))); var wirelessAdapter = new WirelessAdapter(ModuleLogger(nameof(WirelessAdapter)));
var operation = new ShellOperation( var operation = new ShellOperation(
actionCenter, actionCenter,

View file

@ -20,6 +20,7 @@ namespace SafeExamBrowser.Client.Operations
private IActionCenter actionCenter; private IActionCenter actionCenter;
private ILogger logger; private ILogger logger;
private ITaskbar taskbar; private ITaskbar taskbar;
private ITaskView taskView;
private IUserInterfaceFactory uiFactory; private IUserInterfaceFactory uiFactory;
public override event ActionRequiredEventHandler ActionRequired { add { } remove { } } public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
@ -30,11 +31,13 @@ namespace SafeExamBrowser.Client.Operations
ClientContext context, ClientContext context,
ILogger logger, ILogger logger,
ITaskbar taskbar, ITaskbar taskbar,
ITaskView taskView,
IUserInterfaceFactory uiFactory) : base(context) IUserInterfaceFactory uiFactory) : base(context)
{ {
this.actionCenter = actionCenter; this.actionCenter = actionCenter;
this.logger = logger; this.logger = logger;
this.taskbar = taskbar; this.taskbar = taskbar;
this.taskView = taskView;
this.uiFactory = uiFactory; this.uiFactory = uiFactory;
} }
@ -55,6 +58,8 @@ namespace SafeExamBrowser.Client.Operations
taskbar.AddApplicationControl(uiFactory.CreateApplicationControl(Context.Browser, Location.Taskbar), true); taskbar.AddApplicationControl(uiFactory.CreateApplicationControl(Context.Browser, Location.Taskbar), true);
} }
taskView.Add(Context.Browser);
return OperationResult.Success; return OperationResult.Success;
} }

View file

@ -4,10 +4,12 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Controls" xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Controls"
mc:Ignorable="d" Title="ActionCenter" Height="1000" Width="400" Background="#EEF0F0F0" AllowsTransparency="True" WindowStyle="None" Topmost="True" ResizeMode="NoResize"> mc:Ignorable="d" Title="ActionCenter" Height="1000" Width="400" Background="{DynamicResource BackgroundTransparentBrush}"
AllowsTransparency="True" WindowStyle="None" Topmost="True" ResizeMode="NoResize">
<Window.Resources> <Window.Resources>
<ResourceDictionary> <ResourceDictionary>
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="./Templates/Colors.xaml" />
<ResourceDictionary Source="./Templates/ScrollViewers.xaml" /> <ResourceDictionary Source="./Templates/ScrollViewers.xaml" />
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
</ResourceDictionary> </ResourceDictionary>

View file

@ -20,8 +20,8 @@
<RowDefinition Height="*" /> <RowDefinition Height="*" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </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" /> <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> </Grid>
</UserControl> </UserControl>

View file

@ -0,0 +1,21 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Desktop.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.Desktop.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="Border" Background="{StaticResource BackgroundTransparentEmphasisBrush}" BorderBrush="DarkGray" BorderThickness="2" 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.Desktop.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()
{
Border.Visibility = Visibility.Hidden;
Icon.MaxWidth = 40;
Title.FontWeight = FontWeights.Normal;
}
internal void Select()
{
Border.Visibility = Visibility.Visible;
Icon.MaxWidth = 50;
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

@ -4,7 +4,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop" xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop"
mc:Ignorable="d" Title="{Binding Path=WindowTitle}" Background="Black" Foreground="White" Height="500" Width="1100" MinHeight="350" mc:Ignorable="d" Title="{Binding Path=WindowTitle}" Background="Black" Foreground="White" Height="750" Width="1000" MinHeight="350"
MinWidth="350" WindowStartupLocation="CenterScreen" Icon="./Images/LogNotification.ico"> MinWidth="350" WindowStartupLocation="CenterScreen" Icon="./Images/LogNotification.ico">
<Window.Resources> <Window.Resources>
<ResourceDictionary> <ResourceDictionary>

View file

@ -142,6 +142,9 @@
<Compile Include="Controls\TaskbarWirelessNetworkControl.xaml.cs"> <Compile Include="Controls\TaskbarWirelessNetworkControl.xaml.cs">
<DependentUpon>TaskbarWirelessNetworkControl.xaml</DependentUpon> <DependentUpon>TaskbarWirelessNetworkControl.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Controls\TaskViewInstanceControl.xaml.cs">
<DependentUpon>TaskViewInstanceControl.xaml</DependentUpon>
</Compile>
<Compile Include="LockScreen.xaml.cs"> <Compile Include="LockScreen.xaml.cs">
<DependentUpon>LockScreen.xaml</DependentUpon> <DependentUpon>LockScreen.xaml</DependentUpon>
</Compile> </Compile>
@ -314,6 +317,10 @@
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
</Resource> </Resource>
<Page Include="Controls\TaskViewInstanceControl.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="LockScreen.xaml"> <Page Include="LockScreen.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>

View file

@ -4,8 +4,15 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop" xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop"
mc:Ignorable="d" AllowsTransparency="True" Background="#AA000000" BorderBrush="DodgerBlue" BorderThickness="1" Title="TaskView" mc:Ignorable="d" AllowsTransparency="True" Background="{DynamicResource BackgroundTransparentBrush}" BorderBrush="DodgerBlue" BorderThickness="1"
Topmost="True" Height="450" SizeToContent="WidthAndHeight" Width="800" WindowStartupLocation="CenterScreen" WindowStyle="None"> 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> <Grid>
<StackPanel Name="Rows" Margin="10" Orientation="Vertical" /> <StackPanel Name="Rows" Margin="10" Orientation="Vertical" />
</Grid> </Grid>

View file

@ -11,23 +11,24 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using SafeExamBrowser.Applications.Contracts; using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell; using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Shared.Utilities; using SafeExamBrowser.UserInterface.Desktop.Controls;
namespace SafeExamBrowser.UserInterface.Desktop namespace SafeExamBrowser.UserInterface.Desktop
{ {
public partial class TaskView : Window, ITaskView public partial class TaskView : Window, ITaskView
{ {
private IList<IApplicationInstance> instances; private LinkedListNode<TaskViewInstanceControl> current;
private LinkedList<TaskViewInstanceControl> controls;
private List<IApplicationInstance> instances;
public TaskView() public TaskView()
{ {
InitializeComponent(); controls = new LinkedList<TaskViewInstanceControl>();
instances = new List<IApplicationInstance>(); instances = new List<IApplicationInstance>();
InitializeComponent();
} }
public void Add(IApplication application) public void Add(IApplication application)
@ -44,61 +45,84 @@ namespace SafeExamBrowser.UserInterface.Desktop
private void Application_InstanceStarted(IApplicationInstance instance) private void Application_InstanceStarted(IApplicationInstance instance)
{ {
Dispatcher.InvokeAsync(() => Dispatcher.InvokeAsync(() => Add(instance));
{
instance.IconChanged += Instance_IconChanged;
instance.NameChanged += Instance_NameChanged;
instance.Terminated += Instance_Terminated;
instances.Add(instance);
Update();
});
} }
private void Activator_Deactivated() private void Activator_Deactivated()
{ {
Dispatcher.InvokeAsync(Hide); Dispatcher.InvokeAsync(ActivateAndHide);
} }
private void Activator_Next() private void Activator_Next()
{ {
Dispatcher.InvokeAsync(ShowConditional); Dispatcher.InvokeAsync(SelectNext);
} }
private void Activator_Previous() private void Activator_Previous()
{ {
Dispatcher.InvokeAsync(ShowConditional); Dispatcher.InvokeAsync(SelectPrevious);
}
private void Instance_IconChanged(IconResource icon)
{
// TODO Dispatcher.InvokeAsync(...);
}
private void Instance_NameChanged(string name)
{
// TODO Dispatcher.InvokeAsync(...);
} }
private void Instance_Terminated(InstanceIdentifier id) private void Instance_Terminated(InstanceIdentifier id)
{ {
Dispatcher.InvokeAsync(() => Dispatcher.InvokeAsync(() => Remove(id));
{ }
var instance = instances.FirstOrDefault(i => i.Id == id);
if (instance != default(IApplicationInstance)) private void ActivateAndHide()
{ {
instances.Remove(instance); Activate();
Update(); 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() private void ShowConditional()
{ {
if (Visibility != Visibility.Visible && instances.Any()) if (instances.Any() && Visibility != Visibility.Visible)
{ {
Show(); Show();
Activate();
} }
} }
@ -107,6 +131,7 @@ namespace SafeExamBrowser.UserInterface.Desktop
var max = Math.Ceiling(Math.Sqrt(instances.Count)); var max = Math.Ceiling(Math.Sqrt(instances.Count));
var stack = new Stack<IApplicationInstance>(instances); var stack = new Stack<IApplicationInstance>(instances);
controls.Clear();
Rows.Children.Clear(); Rows.Children.Clear();
for (var rowCount = 0; rowCount < max && stack.Any(); rowCount++) for (var rowCount = 0; rowCount < max && stack.Any(); rowCount++)
@ -118,12 +143,16 @@ namespace SafeExamBrowser.UserInterface.Desktop
for (var columnIndex = 0; columnIndex < max && stack.Any(); columnIndex++) for (var columnIndex = 0; columnIndex < max && stack.Any(); columnIndex++)
{ {
var instance = stack.Pop(); var instance = stack.Pop();
var control = BuildInstanceControl(instance); var control = new TaskViewInstanceControl(instance);
controls.AddLast(control);
row.Children.Add(control); row.Children.Add(control);
} }
} }
current = controls.First;
current?.Value.Select();
UpdateLayout(); UpdateLayout();
Left = (SystemParameters.WorkArea.Width - Width) / 2 + SystemParameters.WorkArea.Left; Left = (SystemParameters.WorkArea.Width - Width) / 2 + SystemParameters.WorkArea.Left;
@ -134,28 +163,5 @@ namespace SafeExamBrowser.UserInterface.Desktop
Hide(); Hide();
} }
} }
private UIElement BuildInstanceControl(IApplicationInstance instance)
{
var border = new Border();
var stackPanel = new StackPanel();
var icon = IconResourceLoader.Load(instance.Icon);
border.BorderBrush = Brushes.White;
border.BorderThickness = new Thickness(1);
border.Height = 150;
border.Margin = new Thickness(5);
border.Padding = new Thickness(2);
border.Width = 250;
border.Child = stackPanel;
stackPanel.HorizontalAlignment = HorizontalAlignment.Center;
stackPanel.Orientation = Orientation.Vertical;
stackPanel.VerticalAlignment = VerticalAlignment.Center;
stackPanel.Children.Add(new ContentControl { Content = icon, MaxWidth = 50 });
stackPanel.Children.Add(new TextBlock(new Run($"Instance {instance.Name ?? "NULL"}") { Foreground = Brushes.White, FontWeight = FontWeights.Bold }));
return border;
}
} }
} }

View file

@ -5,10 +5,12 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Controls" xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Controls"
xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:s="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d" Title="Taskbar" Background="#FFF0F0F0" Height="40" Width="750" WindowStyle="None" Topmost="True" ResizeMode="NoResize" Icon="./Images/SafeExamBrowser.ico"> mc:Ignorable="d" Title="Taskbar" Background="{DynamicResource BackgroundBrush}" Height="40" Width="750" WindowStyle="None" Topmost="True"
ResizeMode="NoResize" Icon="./Images/SafeExamBrowser.ico">
<Window.Resources> <Window.Resources>
<ResourceDictionary> <ResourceDictionary>
<ResourceDictionary.MergedDictionaries> <ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="./Templates/Colors.xaml" />
<ResourceDictionary Source="./Templates/ScrollViewers.xaml" /> <ResourceDictionary Source="./Templates/ScrollViewers.xaml" />
</ResourceDictionary.MergedDictionaries> </ResourceDictionary.MergedDictionaries>
</ResourceDictionary> </ResourceDictionary>
@ -26,7 +28,7 @@
</ScrollViewer> </ScrollViewer>
<StackPanel Grid.Column="1" x:Name="NotificationStackPanel" Orientation="Horizontal" VerticalAlignment="Stretch" /> <StackPanel Grid.Column="1" x:Name="NotificationStackPanel" Orientation="Horizontal" VerticalAlignment="Stretch" />
<StackPanel Grid.Column="2" x:Name="SystemControlStackPanel" 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" /> <local:TaskbarQuitButton Grid.Column="4" x:Name="QuitButton" />
</Grid> </Grid>
</Window> </Window>

View file

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

View file

@ -4,7 +4,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile" xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile"
mc:Ignorable="d" Title="{Binding Path=WindowTitle}" Background="Black" Foreground="White" Height="500" Width="1100" MinHeight="350" mc:Ignorable="d" Title="{Binding Path=WindowTitle}" Background="Black" Foreground="White" Height="750" Width="1000" MinHeight="350"
MinWidth="350" WindowState="Maximized" WindowStartupLocation="CenterScreen" Icon="./Images/LogNotification.ico"> MinWidth="350" WindowState="Maximized" WindowStartupLocation="CenterScreen" Icon="./Images/LogNotification.ico">
<Window.Resources> <Window.Resources>
<ResourceDictionary> <ResourceDictionary>

View file

@ -342,7 +342,7 @@ namespace SafeExamBrowser.WindowsApi
public void RestoreWindow(IntPtr window) public void RestoreWindow(IntPtr window)
{ {
User32.ShowWindow(window, (int)ShowWindowCommand.Restore); User32.ShowWindow(window, (int) ShowWindowCommand.Restore);
} }
public bool ResumeThread(int threadId) public bool ResumeThread(int threadId)

View file

@ -69,15 +69,18 @@ namespace SafeExamBrowser.WindowsApi
{ {
try try
{ {
var success = User32.BringWindowToTop(process.MainWindowHandle); var success = true;
var placement = new WINDOWPLACEMENT(); var placement = new WINDOWPLACEMENT();
success &= User32.BringWindowToTop(process.MainWindowHandle);
success &= User32.SetForegroundWindow(process.MainWindowHandle);
placement.length = Marshal.SizeOf(placement); placement.length = Marshal.SizeOf(placement);
User32.GetWindowPlacement(process.MainWindowHandle, ref placement); User32.GetWindowPlacement(process.MainWindowHandle, ref placement);
if (placement.showCmd == (int) ShowWindowCommand.ShowMinimized) if (placement.showCmd == (int) ShowWindowCommand.ShowMinimized)
{ {
success &= User32.ShowWindow(process.MainWindowHandle, (int) ShowWindowCommand.Restore); success &= User32.ShowWindowAsync(process.MainWindowHandle, (int) ShowWindowCommand.Restore);
} }
return success; return success;

View file

@ -59,6 +59,7 @@ namespace SafeExamBrowser.WindowsApi
logger.Info($"Attempting to start process '{path}'..."); logger.Info($"Attempting to start process '{path}'...");
// TODO: Fails for certain processes, whereas Process.Start() does not -> use different API?! Use (declare?) CreateProcessW / CreateProcessA?
var success = Kernel32.CreateProcess(null, commandLine, IntPtr.Zero, IntPtr.Zero, true, Constant.NORMAL_PRIORITY_CLASS, IntPtr.Zero, null, ref startupInfo, ref processInfo); var success = Kernel32.CreateProcess(null, commandLine, IntPtr.Zero, IntPtr.Zero, true, Constant.NORMAL_PRIORITY_CLASS, IntPtr.Zero, null, ref startupInfo, ref processInfo);
if (!success) if (!success)

View file

@ -83,6 +83,9 @@ namespace SafeExamBrowser.WindowsApi
[DllImport("user32.dll", SetLastError = true)] [DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); internal static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)] [DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, EventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags); internal static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, EventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
@ -92,6 +95,9 @@ namespace SafeExamBrowser.WindowsApi
[DllImport("user32.dll", SetLastError = true)] [DllImport("user32.dll", SetLastError = true)]
internal static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); internal static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll", SetLastError = true)]
internal static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll", SetLastError = true)] [DllImport("user32.dll", SetLastError = true)]
internal static extern bool SwitchDesktop(IntPtr hDesktop); internal static extern bool SwitchDesktop(IntPtr hDesktop);