Resolved dependencies from browser API on UI.

This commit is contained in:
dbuechel 2019-08-30 12:30:00 +02:00
parent fd20d0d638
commit affd5de6a7
32 changed files with 245 additions and 276 deletions

View file

@ -0,0 +1,15 @@
/*
* 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/.
*/
namespace SafeExamBrowser.Applications.Contracts.Events
{
/// <summary>
/// Event handler used to inform about the existence of a new <see cref="IApplicationInstance"/>.
/// </summary>
public delegate void InstanceStartedEventHandler(IApplicationInstance instance);
}

View file

@ -11,7 +11,7 @@ using SafeExamBrowser.Core.Contracts;
namespace SafeExamBrowser.Applications.Contracts.Events namespace SafeExamBrowser.Applications.Contracts.Events
{ {
/// <summary> /// <summary>
/// Event handler used to indicate that an <see cref="IApplicationInstance"/> with a particular ID has terminated. /// Event handler used to indicate that an <see cref="IApplicationInstance"/> has terminated.
/// </summary> /// </summary>
public delegate void InstanceTerminatedEventHandler(InstanceIdentifier id); public delegate void InstanceTerminatedEventHandler(InstanceIdentifier id);
} }

View file

@ -6,24 +6,30 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
using SafeExamBrowser.Applications.Contracts.Events;
namespace SafeExamBrowser.Applications.Contracts namespace SafeExamBrowser.Applications.Contracts
{ {
/// <summary> /// <summary>
/// Controls the lifetime and functionality of a (third-party) application which can be accessed via the <see cref="ITaskbar"/>. /// Controls the lifetime and functionality of a (third-party) application.
/// </summary> /// </summary>
public interface IApplicationController public interface IApplication
{ {
/// <summary>
/// Provides information about the application.
/// </summary>
IApplicationInfo Info { get; }
/// <summary>
/// Fired when a new <see cref="IApplicationInstance"/> has started.
/// </summary>
event InstanceStartedEventHandler InstanceStarted;
/// <summary> /// <summary>
/// Performs any initialization work, if necessary. /// Performs any initialization work, if necessary.
/// </summary> /// </summary>
void Initialize(); void Initialize();
// TODO
///// <summary>
///// Registers an application control for this application.
///// </summary>
//void RegisterApplicationControl(IApplicationControl control);
/// <summary> /// <summary>
/// Starts the execution of the application. /// Starts the execution of the application.
/// </summary> /// </summary>

View file

@ -22,7 +22,7 @@ namespace SafeExamBrowser.Applications.Contracts
InstanceIdentifier Id { get; } InstanceIdentifier Id { get; }
/// <summary> /// <summary>
/// The name or (document) title of the application instance. /// The name or document title of the application instance.
/// </summary> /// </summary>
string Name { get; } string Name { get; }
@ -41,10 +41,9 @@ namespace SafeExamBrowser.Applications.Contracts
/// </summary> /// </summary>
event NameChangedEventHandler NameChanged; event NameChangedEventHandler NameChanged;
// TODO /// <summary>
///// <summary> /// Makes this instance the currently active one and brings it to the foreground.
///// The main window of the application instance. /// </summary>
///// </summary> void Activate();
//IWindow Window { get; }
} }
} }

View file

@ -54,9 +54,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Events\IconChangedEventHandler.cs" /> <Compile Include="Events\IconChangedEventHandler.cs" />
<Compile Include="Events\InstanceStartedEventHandler.cs" />
<Compile Include="Events\InstanceTerminatedEventHandler.cs" /> <Compile Include="Events\InstanceTerminatedEventHandler.cs" />
<Compile Include="Events\NameChangedEventHandler.cs" /> <Compile Include="Events\NameChangedEventHandler.cs" />
<Compile Include="IApplicationController.cs" /> <Compile Include="IApplication.cs" />
<Compile Include="IApplicationInfo.cs" /> <Compile Include="IApplicationInfo.cs" />
<Compile Include="IApplicationInstance.cs" /> <Compile Include="IApplicationInstance.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />

View file

@ -13,7 +13,7 @@ namespace SafeExamBrowser.Browser.Contracts
/// <summary> /// <summary>
/// Controls the lifetime and functionality of the browser application. /// Controls the lifetime and functionality of the browser application.
/// </summary> /// </summary>
public interface IBrowserApplicationController : IApplicationController public interface IBrowserApplication : IApplication
{ {
/// <summary> /// <summary>
/// Event fired when the browser application detects a download request for an application configuration file. /// Event fired when the browser application detects a download request for an application configuration file.

View file

@ -56,7 +56,7 @@
<Compile Include="DownloadEventArgs.cs" /> <Compile Include="DownloadEventArgs.cs" />
<Compile Include="DownloadFinishedCallback.cs" /> <Compile Include="DownloadFinishedCallback.cs" />
<Compile Include="DownloadRequestedEventHandler.cs" /> <Compile Include="DownloadRequestedEventHandler.cs" />
<Compile Include="IBrowserApplicationController.cs" /> <Compile Include="IBrowserApplication.cs" />
<Compile Include="ProgressChangedEventHandler.cs" /> <Compile Include="ProgressChangedEventHandler.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>

View file

@ -12,6 +12,7 @@ using System.Linq;
using CefSharp; using CefSharp;
using CefSharp.WinForms; using CefSharp.WinForms;
using SafeExamBrowser.Applications.Contracts; using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Applications.Contracts.Events;
using SafeExamBrowser.Browser.Contracts; using SafeExamBrowser.Browser.Contracts;
using SafeExamBrowser.Browser.Events; using SafeExamBrowser.Browser.Events;
using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Configuration.Contracts;
@ -20,27 +21,28 @@ using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.UserInterface.Contracts; using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.MessageBox; using SafeExamBrowser.UserInterface.Contracts.MessageBox;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using BrowserSettings = SafeExamBrowser.Configuration.Contracts.Settings.BrowserSettings; using BrowserSettings = SafeExamBrowser.Configuration.Contracts.Settings.BrowserSettings;
namespace SafeExamBrowser.Browser namespace SafeExamBrowser.Browser
{ {
public class BrowserApplicationController : IBrowserApplicationController public class BrowserApplication : IBrowserApplication
{ {
private int instanceIdCounter = default(int); private int instanceIdCounter = default(int);
private AppConfig appConfig; private AppConfig appConfig;
private IList<IApplicationControl> controls; private List<BrowserApplicationInstance> instances;
private IList<IApplicationInstance> instances;
private IMessageBox messageBox; private IMessageBox messageBox;
private IModuleLogger logger; private IModuleLogger logger;
private BrowserSettings settings; private BrowserSettings settings;
private IText text; private IText text;
private IUserInterfaceFactory uiFactory; private IUserInterfaceFactory uiFactory;
public event DownloadRequestedEventHandler ConfigurationDownloadRequested; public IApplicationInfo Info { get; private set; }
public BrowserApplicationController( public event DownloadRequestedEventHandler ConfigurationDownloadRequested;
public event InstanceStartedEventHandler InstanceStarted;
public BrowserApplication(
AppConfig appConfig, AppConfig appConfig,
BrowserSettings settings, BrowserSettings settings,
IMessageBox messageBox, IMessageBox messageBox,
@ -49,8 +51,7 @@ namespace SafeExamBrowser.Browser
IUserInterfaceFactory uiFactory) IUserInterfaceFactory uiFactory)
{ {
this.appConfig = appConfig; this.appConfig = appConfig;
this.controls = new List<IApplicationControl>(); this.instances = new List<BrowserApplicationInstance>();
this.instances = new List<IApplicationInstance>();
this.logger = logger; this.logger = logger;
this.messageBox = messageBox; this.messageBox = messageBox;
this.settings = settings; this.settings = settings;
@ -63,20 +64,18 @@ namespace SafeExamBrowser.Browser
var cefSettings = InitializeCefSettings(); var cefSettings = InitializeCefSettings();
var success = Cef.Initialize(cefSettings, true, default(IApp)); var success = Cef.Initialize(cefSettings, true, default(IApp));
logger.Info("Initialized browser."); Info = new BrowserApplicationInfo();
if (!success) if (success)
{
logger.Info("Initialized browser.");
}
else
{ {
throw new Exception("Failed to initialize browser!"); throw new Exception("Failed to initialize browser!");
} }
} }
public void RegisterApplicationControl(IApplicationControl control)
{
control.Clicked += ApplicationControl_Clicked;
controls.Add(control);
}
public void Start() public void Start()
{ {
CreateNewInstance(); CreateNewInstance();
@ -87,13 +86,11 @@ namespace SafeExamBrowser.Browser
foreach (var instance in instances) foreach (var instance in instances)
{ {
instance.Terminated -= Instance_Terminated; instance.Terminated -= Instance_Terminated;
// TODO instance.Window.Close(); instance.Terminate();
logger.Info($"Terminated browser instance {instance.Id}."); logger.Info($"Terminated browser instance {instance.Id}.");
} }
Cef.Shutdown(); Cef.Shutdown();
logger.Info("Terminated browser."); logger.Info("Terminated browser.");
} }
@ -105,35 +102,17 @@ namespace SafeExamBrowser.Browser
var startUrl = url ?? settings.StartUrl; var startUrl = url ?? settings.StartUrl;
var instance = new BrowserApplicationInstance(appConfig, settings, id, isMainInstance, messageBox, instanceLogger, text, uiFactory, startUrl); var instance = new BrowserApplicationInstance(appConfig, settings, id, isMainInstance, messageBox, instanceLogger, text, uiFactory, startUrl);
instance.Initialize();
instance.ConfigurationDownloadRequested += (fileName, args) => ConfigurationDownloadRequested?.Invoke(fileName, args); instance.ConfigurationDownloadRequested += (fileName, args) => ConfigurationDownloadRequested?.Invoke(fileName, args);
instance.IconChanged += Instance_IconChanged;
instance.NameChanged += Instance_NameChanged;
instance.PopupRequested += Instance_PopupRequested; instance.PopupRequested += Instance_PopupRequested;
instance.Terminated += Instance_Terminated; instance.Terminated += Instance_Terminated;
instance.Initialize();
instances.Add(instance); instances.Add(instance);
instance.Window.Show(); InstanceStarted?.Invoke(instance);
logger.Info($"Created browser instance {instance.Id}."); logger.Info($"Created browser instance {instance.Id}.");
} }
private void Instance_NameChanged(string name)
{
foreach (var control in controls)
{
// TODO
}
}
private void Instance_IconChanged(IIconResource icon)
{
foreach (var control in controls)
{
// TODO
}
}
private CefSettings InitializeCefSettings() private CefSettings InitializeCefSettings()
{ {
var warning = logger.LogLevel == LogLevel.Warning; var warning = logger.LogLevel == LogLevel.Warning;
@ -156,18 +135,6 @@ namespace SafeExamBrowser.Browser
return cefSettings; return cefSettings;
} }
private void ApplicationControl_Clicked(InstanceIdentifier id = null)
{
if (id == null)
{
CreateNewInstance();
}
else
{
// TODO instances.FirstOrDefault(i => i.Id == id)?.Window?.BringToForeground();
}
}
private void Instance_PopupRequested(PopupRequestedEventArgs args) private void Instance_PopupRequested(PopupRequestedEventArgs args)
{ {
logger.Info($"Received request to create new instance for '{args.Url}'..."); logger.Info($"Received request to create new instance for '{args.Url}'...");

View file

@ -21,7 +21,6 @@ using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.UserInterface.Contracts; using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Browser; using SafeExamBrowser.UserInterface.Contracts.Browser;
using SafeExamBrowser.UserInterface.Contracts.MessageBox; using SafeExamBrowser.UserInterface.Contracts.MessageBox;
using SafeExamBrowser.UserInterface.Contracts.Windows;
namespace SafeExamBrowser.Browser namespace SafeExamBrowser.Browser
{ {
@ -49,7 +48,6 @@ namespace SafeExamBrowser.Browser
public InstanceIdentifier Id { get; private set; } public InstanceIdentifier Id { get; private set; }
public string Name { get; private set; } public string Name { get; private set; }
public IWindow Window { get { return window; } }
public event DownloadRequestedEventHandler ConfigurationDownloadRequested; public event DownloadRequestedEventHandler ConfigurationDownloadRequested;
public event IconChangedEventHandler IconChanged; public event IconChangedEventHandler IconChanged;
@ -80,6 +78,11 @@ namespace SafeExamBrowser.Browser
this.url = url; this.url = url;
} }
public void Activate()
{
window?.BringToForeground();
}
internal void Initialize() internal void Initialize()
{ {
var contextMenuHandler = new ContextMenuHandler(); var contextMenuHandler = new ContextMenuHandler();
@ -118,10 +121,16 @@ namespace SafeExamBrowser.Browser
window.ZoomOutRequested += ZoomOutRequested; window.ZoomOutRequested += ZoomOutRequested;
window.ZoomResetRequested += ZoomResetRequested; window.ZoomResetRequested += ZoomResetRequested;
window.UpdateZoomLevel(CalculateZoomPercentage()); window.UpdateZoomLevel(CalculateZoomPercentage());
window.Show();
logger.Debug("Initialized browser window."); logger.Debug("Initialized browser window.");
} }
internal void Terminate()
{
window?.Close();
}
private void Control_AddressChanged(string address) private void Control_AddressChanged(string address)
{ {
logger.Debug($"Navigated to '{address}'."); logger.Debug($"Navigated to '{address}'.");

View file

@ -64,7 +64,7 @@
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="BrowserApplicationController.cs" /> <Compile Include="BrowserApplication.cs" />
<Compile Include="BrowserApplicationInfo.cs" /> <Compile Include="BrowserApplicationInfo.cs" />
<Compile Include="BrowserApplicationInstance.cs" /> <Compile Include="BrowserApplicationInstance.cs" />
<Compile Include="BrowserInstanceIdentifier.cs" /> <Compile Include="BrowserInstanceIdentifier.cs" />

View file

@ -25,9 +25,9 @@ namespace SafeExamBrowser.Client.Contracts
AppConfig AppConfig { set; } AppConfig AppConfig { set; }
/// <summary> /// <summary>
/// The controller for the browser application. /// The browser application.
/// </summary> /// </summary>
IBrowserApplicationController Browser { set; } IBrowserApplication Browser { set; }
/// <summary> /// <summary>
/// The client host used for communication handling. /// The client host used for communication handling.

View file

@ -35,7 +35,7 @@ namespace SafeExamBrowser.Client.UnitTests
{ {
private AppConfig appConfig; private AppConfig appConfig;
private Mock<IActionCenter> actionCenter; private Mock<IActionCenter> actionCenter;
private Mock<IBrowserApplicationController> browserController; private Mock<IBrowserApplication> browserController;
private Mock<IClientHost> clientHost; private Mock<IClientHost> clientHost;
private Mock<IDisplayMonitor> displayMonitor; private Mock<IDisplayMonitor> displayMonitor;
private Mock<IExplorerShell> explorerShell; private Mock<IExplorerShell> explorerShell;
@ -61,7 +61,7 @@ namespace SafeExamBrowser.Client.UnitTests
{ {
appConfig = new AppConfig(); appConfig = new AppConfig();
actionCenter = new Mock<IActionCenter>(); actionCenter = new Mock<IActionCenter>();
browserController = new Mock<IBrowserApplicationController>(); browserController = new Mock<IBrowserApplication>();
clientHost = new Mock<IClientHost>(); clientHost = new Mock<IClientHost>();
displayMonitor = new Mock<IDisplayMonitor>(); displayMonitor = new Mock<IDisplayMonitor>();
explorerShell = new Mock<IExplorerShell>(); explorerShell = new Mock<IExplorerShell>();

View file

@ -20,8 +20,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
public class BrowserOperationTests public class BrowserOperationTests
{ {
private Mock<IActionCenter> actionCenter; private Mock<IActionCenter> actionCenter;
private Mock<IApplicationController> controller; private Mock<IApplication> application;
private Mock<IApplicationInfo> appInfo;
private Mock<ILogger> logger; private Mock<ILogger> logger;
private Mock<ITaskbar> taskbar; private Mock<ITaskbar> taskbar;
private Mock<IUserInterfaceFactory> uiFactory; private Mock<IUserInterfaceFactory> uiFactory;
@ -32,13 +31,12 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
public void Initialize() public void Initialize()
{ {
actionCenter = new Mock<IActionCenter>(); actionCenter = new Mock<IActionCenter>();
controller = new Mock<IApplicationController>(); application = new Mock<IApplication>();
appInfo = new Mock<IApplicationInfo>();
logger = new Mock<ILogger>(); logger = new Mock<ILogger>();
taskbar = new Mock<ITaskbar>(); taskbar = new Mock<ITaskbar>();
uiFactory = new Mock<IUserInterfaceFactory>(); uiFactory = new Mock<IUserInterfaceFactory>();
sut = new BrowserOperation(actionCenter.Object, controller.Object, appInfo.Object, logger.Object, taskbar.Object, uiFactory.Object); sut = new BrowserOperation(actionCenter.Object, application.Object, logger.Object, taskbar.Object, uiFactory.Object);
} }
[TestMethod] [TestMethod]
@ -46,7 +44,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
{ {
sut.Perform(); sut.Perform();
controller.Verify(c => c.Initialize(), Times.Once); application.Verify(c => c.Initialize(), Times.Once);
// TODO controller.Verify(c => c.RegisterApplicationControl(It.IsAny<IApplicationControl>()), Times.Exactly(2)); // TODO controller.Verify(c => c.RegisterApplicationControl(It.IsAny<IApplicationControl>()), Times.Exactly(2));
actionCenter.Verify(a => a.AddApplicationControl(It.IsAny<IApplicationControl>()), Times.Once); actionCenter.Verify(a => a.AddApplicationControl(It.IsAny<IApplicationControl>()), Times.Once);
taskbar.Verify(t => t.AddApplicationControl(It.IsAny<IApplicationControl>()), Times.Once); taskbar.Verify(t => t.AddApplicationControl(It.IsAny<IApplicationControl>()), Times.Once);
@ -56,7 +54,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
public void MustRevertCorrectly() public void MustRevertCorrectly()
{ {
sut.Revert(); sut.Revert();
controller.Verify(c => c.Terminate(), Times.Once); application.Verify(c => c.Terminate(), Times.Once);
} }
} }
} }

View file

@ -50,7 +50,7 @@ namespace SafeExamBrowser.Client
private IWindowMonitor windowMonitor; private IWindowMonitor windowMonitor;
private AppConfig appConfig; private AppConfig appConfig;
public IBrowserApplicationController Browser { private get; set; } public IBrowserApplication Browser { private get; set; }
public IClientHost ClientHost { private get; set; } public IClientHost ClientHost { private get; set; }
public Guid SessionId { private get; set; } public Guid SessionId { private get; set; }
public Settings Settings { private get; set; } public Settings Settings { private get; set; }

View file

@ -60,7 +60,7 @@ namespace SafeExamBrowser.Client
private UserInterfaceMode uiMode; private UserInterfaceMode uiMode;
private IActionCenter actionCenter; private IActionCenter actionCenter;
private IBrowserApplicationController browserController; private IBrowserApplication browser;
private IClientHost clientHost; private IClientHost clientHost;
private ILogger logger; private ILogger logger;
private IMessageBox messageBox; private IMessageBox messageBox;
@ -199,11 +199,11 @@ namespace SafeExamBrowser.Client
private IOperation BuildBrowserOperation() private IOperation BuildBrowserOperation()
{ {
var moduleLogger = new ModuleLogger(logger, "BrowserController"); var moduleLogger = new ModuleLogger(logger, "BrowserController");
var browserController = new BrowserApplicationController(configuration.AppConfig, configuration.Settings.Browser, messageBox, moduleLogger, text, uiFactory); var browser = new BrowserApplication(configuration.AppConfig, configuration.Settings.Browser, messageBox, moduleLogger, text, uiFactory);
var browserInfo = new BrowserApplicationInfo(); var browserInfo = new BrowserApplicationInfo();
var operation = new BrowserOperation(actionCenter, browserController, browserInfo, logger, taskbar, uiFactory); var operation = new BrowserOperation(actionCenter, browser, logger, taskbar, uiFactory);
this.browserController = browserController; this.browser = browser;
return operation; return operation;
} }
@ -346,7 +346,7 @@ namespace SafeExamBrowser.Client
private void UpdateClientControllerDependencies() private void UpdateClientControllerDependencies()
{ {
ClientController.Browser = browserController; ClientController.Browser = browser;
ClientController.ClientHost = clientHost; ClientController.ClientHost = clientHost;
ClientController.SessionId = configuration.SessionId; ClientController.SessionId = configuration.SessionId;
ClientController.Settings = configuration.Settings; ClientController.Settings = configuration.Settings;

View file

@ -19,8 +19,7 @@ namespace SafeExamBrowser.Client.Operations
internal class BrowserOperation : IOperation internal class BrowserOperation : IOperation
{ {
private IActionCenter actionCenter; private IActionCenter actionCenter;
private IApplicationController browserController; private IApplication browser;
private IApplicationInfo browserInfo;
private ILogger logger; private ILogger logger;
private ITaskbar taskbar; private ITaskbar taskbar;
private IUserInterfaceFactory uiFactory; private IUserInterfaceFactory uiFactory;
@ -30,15 +29,13 @@ namespace SafeExamBrowser.Client.Operations
public BrowserOperation( public BrowserOperation(
IActionCenter actionCenter, IActionCenter actionCenter,
IApplicationController browserController, IApplication browser,
IApplicationInfo browserInfo,
ILogger logger, ILogger logger,
ITaskbar taskbar, ITaskbar taskbar,
IUserInterfaceFactory uiFactory) IUserInterfaceFactory uiFactory)
{ {
this.actionCenter = actionCenter; this.actionCenter = actionCenter;
this.browserController = browserController; this.browser = browser;
this.browserInfo = browserInfo;
this.logger = logger; this.logger = logger;
this.taskbar = taskbar; this.taskbar = taskbar;
this.uiFactory = uiFactory; this.uiFactory = uiFactory;
@ -49,16 +46,10 @@ namespace SafeExamBrowser.Client.Operations
logger.Info("Initializing browser..."); logger.Info("Initializing browser...");
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeBrowser); StatusChanged?.Invoke(TextKey.OperationStatus_InitializeBrowser);
var actionCenterControl = uiFactory.CreateApplicationControl(browserInfo, Location.ActionCenter); browser.Initialize();
var taskbarControl = uiFactory.CreateApplicationControl(browserInfo, Location.Taskbar);
browserController.Initialize(); actionCenter.AddApplicationControl(uiFactory.CreateApplicationControl(browser, Location.ActionCenter));
// TODO taskbar.AddApplicationControl(uiFactory.CreateApplicationControl(browser, Location.Taskbar));
//browserController.RegisterApplicationControl(actionCenterControl);
//browserController.RegisterApplicationControl(taskbarControl);
actionCenter.AddApplicationControl(actionCenterControl);
taskbar.AddApplicationControl(taskbarControl);
return OperationResult.Success; return OperationResult.Success;
} }
@ -68,7 +59,7 @@ namespace SafeExamBrowser.Client.Operations
logger.Info("Terminating browser..."); logger.Info("Terminating browser...");
StatusChanged?.Invoke(TextKey.OperationStatus_TerminateBrowser); StatusChanged?.Invoke(TextKey.OperationStatus_TerminateBrowser);
browserController.Terminate(); browser.Terminate();
return OperationResult.Success; return OperationResult.Success;
} }

View file

@ -29,9 +29,9 @@ namespace SafeExamBrowser.UserInterface.Contracts
IWindow CreateAboutWindow(AppConfig appConfig); IWindow CreateAboutWindow(AppConfig appConfig);
/// <summary> /// <summary>
/// Creates an application control for the specified location, initialized with the given application information. /// Creates an application control for the specified application and location.
/// </summary> /// </summary>
IApplicationControl CreateApplicationControl(IApplicationInfo info, Location location); IApplicationControl CreateApplicationControl(IApplication application, Location location);
/// <summary> /// <summary>
/// Creates a system control which allows to change the audio settings of the computer. /// Creates a system control which allows to change the audio settings of the computer.

View file

@ -6,8 +6,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
using SafeExamBrowser.UserInterface.Contracts.Shell.Events;
namespace SafeExamBrowser.UserInterface.Contracts.Shell namespace SafeExamBrowser.UserInterface.Contracts.Shell
{ {
/// <summary> /// <summary>
@ -15,10 +13,5 @@ namespace SafeExamBrowser.UserInterface.Contracts.Shell
/// </summary> /// </summary>
public interface IApplicationControl public interface IApplicationControl
{ {
/// <summary>
/// Event fired when the user clicked on the application control. If multiple instances of an application are active,
/// the handler should only executed when the user selects one of the instances.
/// </summary>
event ApplicationControlClickedEventHandler Clicked;
} }
} }

View file

@ -14,7 +14,7 @@
</ResourceDictionary> </ResourceDictionary>
</UserControl.Resources> </UserControl.Resources>
<Grid> <Grid>
<Button x:Name="Button" Background="Transparent" Click="Button_Click" Height="40" Padding="10" Template="{StaticResource ActionCenterButton}"> <Button x:Name="Button" Background="Transparent" Height="40" Padding="10" Template="{StaticResource ActionCenterButton}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<ContentControl x:Name="Icon" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0,0,10,0" Width="20" /> <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" /> <TextBlock x:Name="Text" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="5" MaxWidth="350" TextTrimming="CharacterEllipsis" />

View file

@ -6,11 +6,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
using System.Windows; using System;
using System.Windows.Controls; using System.Windows.Controls;
using SafeExamBrowser.Applications.Contracts; using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Core.Contracts; using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell.Events;
using SafeExamBrowser.UserInterface.Shared.Utilities; using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Desktop.Controls namespace SafeExamBrowser.UserInterface.Desktop.Controls
@ -20,7 +19,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
private IApplicationInfo info; private IApplicationInfo info;
private IApplicationInstance instance; private IApplicationInstance instance;
internal event ApplicationControlClickedEventHandler Clicked; internal event EventHandler Clicked;
public ActionCenterApplicationButton(IApplicationInfo info, IApplicationInstance instance = null) public ActionCenterApplicationButton(IApplicationInfo info, IApplicationInstance instance = null)
{ {
@ -35,6 +34,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
{ {
Icon.Content = IconResourceLoader.Load(info.IconResource); Icon.Content = IconResourceLoader.Load(info.IconResource);
Text.Text = instance?.Name ?? info.Name; Text.Text = instance?.Name ?? info.Name;
Button.Click += (o, args) => Clicked?.Invoke(this, EventArgs.Empty);
Button.ToolTip = instance?.Name ?? info.Tooltip; Button.ToolTip = instance?.Name ?? info.Tooltip;
if (instance != null) if (instance != null)
@ -51,16 +51,11 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
private void Instance_NameChanged(string name) private void Instance_NameChanged(string name)
{ {
Dispatcher.Invoke(() => Dispatcher.InvokeAsync(() =>
{ {
Text.Text = name; Text.Text = name;
Button.ToolTip = name; Button.ToolTip = name;
}); });
} }
private void Button_Click(object sender, RoutedEventArgs e)
{
Clicked?.Invoke(instance?.Id);
}
} }
} }

View file

@ -9,34 +9,40 @@
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
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.Contracts.Shell.Events;
namespace SafeExamBrowser.UserInterface.Desktop.Controls namespace SafeExamBrowser.UserInterface.Desktop.Controls
{ {
public partial class ActionCenterApplicationControl : UserControl, IApplicationControl public partial class ActionCenterApplicationControl : UserControl, IApplicationControl
{ {
private IApplicationInfo info; private IApplication application;
public event ApplicationControlClickedEventHandler Clicked; public ActionCenterApplicationControl(IApplication application)
public ActionCenterApplicationControl(IApplicationInfo info)
{ {
this.info = info; this.application = application;
InitializeComponent(); InitializeComponent();
InitializeApplicationControl(info); InitializeApplicationControl();
} }
public void RegisterInstance(IApplicationInstance instance) private void InitializeApplicationControl()
{ {
Dispatcher.Invoke(() => var button = new ActionCenterApplicationButton(application.Info);
{
var button = new ActionCenterApplicationButton(info, instance);
button.Clicked += (id) => Clicked?.Invoke(id); application.InstanceStarted += Application_InstanceStarted;
instance.Terminated += (id) => Instance_OnTerminated(id, button); button.Clicked += (o, args) => application.Start();
ApplicationName.Text = application.Info.Name;
ApplicationButton.Content = button;
}
private void Application_InstanceStarted(IApplicationInstance instance)
{
Dispatcher.InvokeAsync(() =>
{
var button = new ActionCenterApplicationButton(application.Info, instance);
button.Clicked += (o, args) => instance.Activate();
instance.Terminated += (_) => RemoveInstance(button);
InstancePanel.Children.Add(button); InstancePanel.Children.Add(button);
ApplicationName.Visibility = Visibility.Visible; ApplicationName.Visibility = Visibility.Visible;
@ -44,16 +50,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
}); });
} }
private void InitializeApplicationControl(IApplicationInfo info) private void RemoveInstance(ActionCenterApplicationButton button)
{
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(() => Dispatcher.InvokeAsync(() =>
{ {

View file

@ -6,59 +6,41 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Threading; using System.Windows.Threading;
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.Contracts.Shell.Events;
using SafeExamBrowser.UserInterface.Shared.Utilities; using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Desktop.Controls namespace SafeExamBrowser.UserInterface.Desktop.Controls
{ {
public partial class TaskbarApplicationControl : UserControl, IApplicationControl public partial class TaskbarApplicationControl : UserControl, IApplicationControl
{ {
private IApplicationInfo info; private IApplication application;
private IList<IApplicationInstance> instances = new List<IApplicationInstance>(); private IApplicationInstance single;
public event ApplicationControlClickedEventHandler Clicked; public TaskbarApplicationControl(IApplication application)
public TaskbarApplicationControl(IApplicationInfo info)
{ {
this.info = info; this.application = application;
InitializeComponent(); InitializeComponent();
InitializeApplicationControl(); 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() private void InitializeApplicationControl()
{ {
var originalBrush = Button.Background; var originalBrush = Button.Background;
Button.ToolTip = info.Tooltip; application.InstanceStarted += Application_InstanceStarted;
Button.Content = IconResourceLoader.Load(info.IconResource);
Button.MouseEnter += (o, args) => InstancePopup.IsOpen = instances.Count > 1; Button.Click += Button_Click;
Button.Content = IconResourceLoader.Load(application.Info.IconResource);
Button.MouseEnter += (o, args) => InstancePopup.IsOpen = InstanceStackPanel.Children.Count > 1;
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => InstancePopup.IsOpen = InstancePopup.IsMouseOver)); Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => InstancePopup.IsOpen = InstancePopup.IsMouseOver));
Button.ToolTip = application.Info.Tooltip;
InstancePopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => InstancePopup.IsOpen = IsMouseOver)); InstancePopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => InstancePopup.IsOpen = IsMouseOver));
InstancePopup.Opened += (o, args) => InstancePopup.Opened += (o, args) =>
@ -74,11 +56,31 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
}; };
} }
private void Application_InstanceStarted(IApplicationInstance instance)
{
Dispatcher.Invoke(() =>
{
var button = new TaskbarApplicationInstanceButton(instance, application.Info);
instance.Terminated += (_) => RemoveInstance(button);
InstanceStackPanel.Children.Add(button);
if (single == default(IApplicationInstance))
{
single = instance;
}
});
}
private void Button_Click(object sender, RoutedEventArgs e) private void Button_Click(object sender, RoutedEventArgs e)
{ {
if (instances.Count <= 1) if (InstanceStackPanel.Children.Count == 0)
{ {
Clicked?.Invoke(instances.FirstOrDefault()?.Id); application.Start();
}
else if (InstanceStackPanel.Children.Count == 1)
{
single.Activate();
} }
else else
{ {
@ -86,12 +88,16 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
} }
} }
private void Instance_OnTerminated(InstanceIdentifier id, TaskbarApplicationInstanceButton instanceButton) private void RemoveInstance(TaskbarApplicationInstanceButton button)
{ {
Dispatcher.InvokeAsync(() => Dispatcher.InvokeAsync(() =>
{ {
instances.Remove(instances.FirstOrDefault(i => i.Id == id)); InstanceStackPanel.Children.Remove(button);
InstanceStackPanel.Children.Remove(instanceButton);
if (InstanceStackPanel.Children.Count == 0)
{
single = default(IApplicationInstance);
}
}); });
} }
} }

View file

@ -14,7 +14,7 @@
</ResourceDictionary> </ResourceDictionary>
</UserControl.Resources> </UserControl.Resources>
<Grid> <Grid>
<Button x:Name="Button" Background="Transparent" Click="Button_Click" Height="40" Padding="10" Template="{StaticResource TaskbarButton}"> <Button x:Name="Button" Background="Transparent" Height="40" Padding="10" Template="{StaticResource TaskbarButton}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<ContentControl x:Name="Icon" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0,0,10,0" Width="20" /> <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" /> <TextBlock x:Name="Text" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="5" MaxWidth="350" TextTrimming="CharacterEllipsis" />

View file

@ -10,7 +10,6 @@ using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using SafeExamBrowser.Applications.Contracts; using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Core.Contracts; using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell.Events;
using SafeExamBrowser.UserInterface.Shared.Utilities; using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Desktop.Controls namespace SafeExamBrowser.UserInterface.Desktop.Controls
@ -20,8 +19,6 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
private IApplicationInfo info; private IApplicationInfo info;
private IApplicationInstance instance; private IApplicationInstance instance;
internal event ApplicationControlClickedEventHandler Clicked;
public TaskbarApplicationInstanceButton(IApplicationInstance instance, IApplicationInfo info) public TaskbarApplicationInstanceButton(IApplicationInstance instance, IApplicationInfo info)
{ {
this.info = info; this.info = info;
@ -33,12 +30,17 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
private void InitializeApplicationInstanceButton() private void InitializeApplicationInstanceButton()
{ {
Icon.Content = IconResourceLoader.Load(info.IconResource); Button.Click += Button_Click;
Text.Text = instance.Name;
Button.ToolTip = instance.Name; Button.ToolTip = instance.Name;
Icon.Content = IconResourceLoader.Load(info.IconResource);
instance.IconChanged += Instance_IconChanged; instance.IconChanged += Instance_IconChanged;
instance.NameChanged += Instance_NameChanged; instance.NameChanged += Instance_NameChanged;
Text.Text = instance.Name;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
instance.Activate();
} }
private void Instance_IconChanged(IIconResource icon) private void Instance_IconChanged(IIconResource icon)
@ -54,10 +56,5 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
Button.ToolTip = name; Button.ToolTip = name;
}); });
} }
private void Button_Click(object sender, RoutedEventArgs e)
{
Clicked?.Invoke(instance.Id);
}
} }
} }

View file

@ -40,15 +40,15 @@ namespace SafeExamBrowser.UserInterface.Desktop
return new AboutWindow(appConfig, text); return new AboutWindow(appConfig, text);
} }
public IApplicationControl CreateApplicationControl(IApplicationInfo info, Location location) public IApplicationControl CreateApplicationControl(IApplication application, Location location)
{ {
if (location == Location.ActionCenter) if (location == Location.ActionCenter)
{ {
return new ActionCenterApplicationControl(info); return new ActionCenterApplicationControl(application);
} }
else else
{ {
return new TaskbarApplicationControl(info); return new TaskbarApplicationControl(application);
} }
} }

View file

@ -14,7 +14,7 @@
</ResourceDictionary> </ResourceDictionary>
</UserControl.Resources> </UserControl.Resources>
<Grid> <Grid>
<Button x:Name="Button" Background="Transparent" Click="Button_Click" Height="60" Padding="10" Template="{StaticResource ActionCenterButton}"> <Button x:Name="Button" Background="Transparent" Height="60" Padding="10" Template="{StaticResource ActionCenterButton}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<ContentControl x:Name="Icon" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0,0,10,0" Width="20" /> <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" /> <TextBlock x:Name="Text" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="5" MaxWidth="350" TextTrimming="CharacterEllipsis" />

View file

@ -6,11 +6,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
using System.Windows; using System;
using System.Windows.Controls; using System.Windows.Controls;
using SafeExamBrowser.Applications.Contracts; using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Core.Contracts; using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell.Events;
using SafeExamBrowser.UserInterface.Shared.Utilities; using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls namespace SafeExamBrowser.UserInterface.Mobile.Controls
@ -20,7 +19,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
private IApplicationInfo info; private IApplicationInfo info;
private IApplicationInstance instance; private IApplicationInstance instance;
internal event ApplicationControlClickedEventHandler Clicked; internal event EventHandler Clicked;
public ActionCenterApplicationButton(IApplicationInfo info, IApplicationInstance instance = null) public ActionCenterApplicationButton(IApplicationInfo info, IApplicationInstance instance = null)
{ {
@ -35,6 +34,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
{ {
Icon.Content = IconResourceLoader.Load(info.IconResource); Icon.Content = IconResourceLoader.Load(info.IconResource);
Text.Text = instance?.Name ?? info.Name; Text.Text = instance?.Name ?? info.Name;
Button.Click += (o, args) => Clicked?.Invoke(this, EventArgs.Empty);
Button.ToolTip = instance?.Name ?? info.Tooltip; Button.ToolTip = instance?.Name ?? info.Tooltip;
if (instance != null) if (instance != null)
@ -51,16 +51,11 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
private void Instance_NameChanged(string name) private void Instance_NameChanged(string name)
{ {
Dispatcher.Invoke(() => Dispatcher.InvokeAsync(() =>
{ {
Text.Text = name; Text.Text = name;
Button.ToolTip = name; Button.ToolTip = name;
}); });
} }
private void Button_Click(object sender, RoutedEventArgs e)
{
Clicked?.Invoke(instance?.Id);
}
} }
} }

View file

@ -9,34 +9,40 @@
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
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.Contracts.Shell.Events;
namespace SafeExamBrowser.UserInterface.Mobile.Controls namespace SafeExamBrowser.UserInterface.Mobile.Controls
{ {
public partial class ActionCenterApplicationControl : UserControl, IApplicationControl public partial class ActionCenterApplicationControl : UserControl, IApplicationControl
{ {
private IApplicationInfo info; private IApplication application;
public event ApplicationControlClickedEventHandler Clicked; public ActionCenterApplicationControl(IApplication application)
public ActionCenterApplicationControl(IApplicationInfo info)
{ {
this.info = info; this.application = application;
InitializeComponent(); InitializeComponent();
InitializeApplicationControl(info); InitializeApplicationControl();
} }
public void RegisterInstance(IApplicationInstance instance) private void InitializeApplicationControl()
{ {
Dispatcher.Invoke(() => var button = new ActionCenterApplicationButton(application.Info);
{
var button = new ActionCenterApplicationButton(info, instance);
button.Clicked += (id) => Clicked?.Invoke(id); application.InstanceStarted += Application_InstanceStarted;
instance.Terminated += (id) => Instance_OnTerminated(id, button); button.Clicked += (o, args) => application.Start();
ApplicationName.Text = application.Info.Name;
ApplicationButton.Content = button;
}
private void Application_InstanceStarted(IApplicationInstance instance)
{
Dispatcher.InvokeAsync(() =>
{
var button = new ActionCenterApplicationButton(application.Info, instance);
button.Clicked += (o, args) => instance.Activate();
instance.Terminated += (_) => RemoveInstance(button);
InstancePanel.Children.Add(button); InstancePanel.Children.Add(button);
ApplicationName.Visibility = Visibility.Visible; ApplicationName.Visibility = Visibility.Visible;
@ -44,16 +50,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
}); });
} }
private void InitializeApplicationControl(IApplicationInfo info) private void RemoveInstance(ActionCenterApplicationButton button)
{
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(() => Dispatcher.InvokeAsync(() =>
{ {

View file

@ -6,59 +6,41 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Threading; using System.Windows.Threading;
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.Contracts.Shell.Events;
using SafeExamBrowser.UserInterface.Shared.Utilities; using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls namespace SafeExamBrowser.UserInterface.Mobile.Controls
{ {
public partial class TaskbarApplicationControl : UserControl, IApplicationControl public partial class TaskbarApplicationControl : UserControl, IApplicationControl
{ {
private IApplicationInfo info; private IApplication application;
private IList<IApplicationInstance> instances = new List<IApplicationInstance>(); private IApplicationInstance single;
public event ApplicationControlClickedEventHandler Clicked; public TaskbarApplicationControl(IApplication application)
public TaskbarApplicationControl(IApplicationInfo info)
{ {
this.info = info; this.application = application;
InitializeComponent(); InitializeComponent();
InitializeApplicationControl(); 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() private void InitializeApplicationControl()
{ {
var originalBrush = Button.Background; var originalBrush = Button.Background;
Button.ToolTip = info.Tooltip; application.InstanceStarted += Application_InstanceStarted;
Button.Content = IconResourceLoader.Load(info.IconResource);
Button.MouseEnter += (o, args) => InstancePopup.IsOpen = instances.Count > 1; Button.Click += Button_Click;
Button.Content = IconResourceLoader.Load(application.Info.IconResource);
Button.MouseEnter += (o, args) => InstancePopup.IsOpen = InstanceStackPanel.Children.Count > 1;
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => InstancePopup.IsOpen = InstancePopup.IsMouseOver)); Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => InstancePopup.IsOpen = InstancePopup.IsMouseOver));
Button.ToolTip = application.Info.Tooltip;
InstancePopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => InstancePopup.IsOpen = IsMouseOver)); InstancePopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => InstancePopup.IsOpen = IsMouseOver));
InstancePopup.Opened += (o, args) => InstancePopup.Opened += (o, args) =>
@ -74,11 +56,31 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
}; };
} }
private void Application_InstanceStarted(IApplicationInstance instance)
{
Dispatcher.Invoke(() =>
{
var button = new TaskbarApplicationInstanceButton(instance, application.Info);
instance.Terminated += (_) => RemoveInstance(button);
InstanceStackPanel.Children.Add(button);
if (single == default(IApplicationInstance))
{
single = instance;
}
});
}
private void Button_Click(object sender, RoutedEventArgs e) private void Button_Click(object sender, RoutedEventArgs e)
{ {
if (instances.Count <= 1) if (InstanceStackPanel.Children.Count == 0)
{ {
Clicked?.Invoke(instances.FirstOrDefault()?.Id); application.Start();
}
else if (InstanceStackPanel.Children.Count == 1)
{
single.Activate();
} }
else else
{ {
@ -86,12 +88,16 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
} }
} }
private void Instance_OnTerminated(InstanceIdentifier id, TaskbarApplicationInstanceButton instanceButton) private void RemoveInstance(TaskbarApplicationInstanceButton button)
{ {
Dispatcher.InvokeAsync(() => Dispatcher.InvokeAsync(() =>
{ {
instances.Remove(instances.FirstOrDefault(i => i.Id == id)); InstanceStackPanel.Children.Remove(button);
InstanceStackPanel.Children.Remove(instanceButton);
if (InstanceStackPanel.Children.Count == 0)
{
single = default(IApplicationInstance);
}
}); });
} }
} }

View file

@ -14,7 +14,7 @@
</ResourceDictionary> </ResourceDictionary>
</UserControl.Resources> </UserControl.Resources>
<Grid> <Grid>
<Button x:Name="Button" Background="Transparent" Click="Button_Click" Height="60" Padding="10" Template="{StaticResource TaskbarButton}"> <Button x:Name="Button" Background="Transparent" Height="60" Padding="10" Template="{StaticResource TaskbarButton}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<ContentControl x:Name="Icon" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0,0,10,0" Width="20" /> <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" /> <TextBlock x:Name="Text" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="5" MaxWidth="350" TextTrimming="CharacterEllipsis" />

View file

@ -10,7 +10,6 @@ using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using SafeExamBrowser.Applications.Contracts; using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Core.Contracts; using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell.Events;
using SafeExamBrowser.UserInterface.Shared.Utilities; using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls namespace SafeExamBrowser.UserInterface.Mobile.Controls
@ -20,8 +19,6 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
private IApplicationInfo info; private IApplicationInfo info;
private IApplicationInstance instance; private IApplicationInstance instance;
internal event ApplicationControlClickedEventHandler Clicked;
public TaskbarApplicationInstanceButton(IApplicationInstance instance, IApplicationInfo info) public TaskbarApplicationInstanceButton(IApplicationInstance instance, IApplicationInfo info)
{ {
this.info = info; this.info = info;
@ -33,12 +30,17 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
private void InitializeApplicationInstanceButton() private void InitializeApplicationInstanceButton()
{ {
Icon.Content = IconResourceLoader.Load(info.IconResource); Button.Click += Button_Click;
Text.Text = instance.Name;
Button.ToolTip = instance.Name; Button.ToolTip = instance.Name;
Icon.Content = IconResourceLoader.Load(info.IconResource);
instance.IconChanged += Instance_IconChanged; instance.IconChanged += Instance_IconChanged;
instance.NameChanged += Instance_NameChanged; instance.NameChanged += Instance_NameChanged;
Text.Text = instance.Name;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
instance.Activate();
} }
private void Instance_IconChanged(IIconResource icon) private void Instance_IconChanged(IIconResource icon)
@ -54,10 +56,5 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
Button.ToolTip = name; Button.ToolTip = name;
}); });
} }
private void Button_Click(object sender, RoutedEventArgs e)
{
Clicked?.Invoke(instance.Id);
}
} }
} }

View file

@ -40,15 +40,15 @@ namespace SafeExamBrowser.UserInterface.Mobile
return new AboutWindow(appConfig, text); return new AboutWindow(appConfig, text);
} }
public IApplicationControl CreateApplicationControl(IApplicationInfo info, Location location) public IApplicationControl CreateApplicationControl(IApplication application, Location location)
{ {
if (location == Location.ActionCenter) if (location == Location.ActionCenter)
{ {
return new ActionCenterApplicationControl(info); return new ActionCenterApplicationControl(application);
} }
else else
{ {
return new TaskbarApplicationControl(info); return new TaskbarApplicationControl(application);
} }
} }