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
{
/// <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>
public delegate void InstanceTerminatedEventHandler(InstanceIdentifier id);
}

View file

@ -6,24 +6,30 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using SafeExamBrowser.Applications.Contracts.Events;
namespace SafeExamBrowser.Applications.Contracts
{
/// <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>
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>
/// Performs any initialization work, if necessary.
/// </summary>
void Initialize();
// TODO
///// <summary>
///// Registers an application control for this application.
///// </summary>
//void RegisterApplicationControl(IApplicationControl control);
/// <summary>
/// Starts the execution of the application.
/// </summary>

View file

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

View file

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

View file

@ -13,7 +13,7 @@ namespace SafeExamBrowser.Browser.Contracts
/// <summary>
/// Controls the lifetime and functionality of the browser application.
/// </summary>
public interface IBrowserApplicationController : IApplicationController
public interface IBrowserApplication : IApplication
{
/// <summary>
/// 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="DownloadFinishedCallback.cs" />
<Compile Include="DownloadRequestedEventHandler.cs" />
<Compile Include="IBrowserApplicationController.cs" />
<Compile Include="IBrowserApplication.cs" />
<Compile Include="ProgressChangedEventHandler.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>

View file

@ -12,6 +12,7 @@ using System.Linq;
using CefSharp;
using CefSharp.WinForms;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Applications.Contracts.Events;
using SafeExamBrowser.Browser.Contracts;
using SafeExamBrowser.Browser.Events;
using SafeExamBrowser.Configuration.Contracts;
@ -20,27 +21,28 @@ using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using BrowserSettings = SafeExamBrowser.Configuration.Contracts.Settings.BrowserSettings;
namespace SafeExamBrowser.Browser
{
public class BrowserApplicationController : IBrowserApplicationController
public class BrowserApplication : IBrowserApplication
{
private int instanceIdCounter = default(int);
private AppConfig appConfig;
private IList<IApplicationControl> controls;
private IList<IApplicationInstance> instances;
private List<BrowserApplicationInstance> instances;
private IMessageBox messageBox;
private IModuleLogger logger;
private BrowserSettings settings;
private IText text;
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,
BrowserSettings settings,
IMessageBox messageBox,
@ -49,8 +51,7 @@ namespace SafeExamBrowser.Browser
IUserInterfaceFactory uiFactory)
{
this.appConfig = appConfig;
this.controls = new List<IApplicationControl>();
this.instances = new List<IApplicationInstance>();
this.instances = new List<BrowserApplicationInstance>();
this.logger = logger;
this.messageBox = messageBox;
this.settings = settings;
@ -63,20 +64,18 @@ namespace SafeExamBrowser.Browser
var cefSettings = InitializeCefSettings();
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!");
}
}
public void RegisterApplicationControl(IApplicationControl control)
{
control.Clicked += ApplicationControl_Clicked;
controls.Add(control);
}
public void Start()
{
CreateNewInstance();
@ -87,13 +86,11 @@ namespace SafeExamBrowser.Browser
foreach (var instance in instances)
{
instance.Terminated -= Instance_Terminated;
// TODO instance.Window.Close();
instance.Terminate();
logger.Info($"Terminated browser instance {instance.Id}.");
}
Cef.Shutdown();
logger.Info("Terminated browser.");
}
@ -105,35 +102,17 @@ namespace SafeExamBrowser.Browser
var startUrl = url ?? settings.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.IconChanged += Instance_IconChanged;
instance.NameChanged += Instance_NameChanged;
instance.PopupRequested += Instance_PopupRequested;
instance.Terminated += Instance_Terminated;
instance.Initialize();
instances.Add(instance);
instance.Window.Show();
InstanceStarted?.Invoke(instance);
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()
{
var warning = logger.LogLevel == LogLevel.Warning;
@ -156,18 +135,6 @@ namespace SafeExamBrowser.Browser
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)
{
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.Browser;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
using SafeExamBrowser.UserInterface.Contracts.Windows;
namespace SafeExamBrowser.Browser
{
@ -49,7 +48,6 @@ namespace SafeExamBrowser.Browser
public InstanceIdentifier Id { get; private set; }
public string Name { get; private set; }
public IWindow Window { get { return window; } }
public event DownloadRequestedEventHandler ConfigurationDownloadRequested;
public event IconChangedEventHandler IconChanged;
@ -80,6 +78,11 @@ namespace SafeExamBrowser.Browser
this.url = url;
}
public void Activate()
{
window?.BringToForeground();
}
internal void Initialize()
{
var contextMenuHandler = new ContextMenuHandler();
@ -118,10 +121,16 @@ namespace SafeExamBrowser.Browser
window.ZoomOutRequested += ZoomOutRequested;
window.ZoomResetRequested += ZoomResetRequested;
window.UpdateZoomLevel(CalculateZoomPercentage());
window.Show();
logger.Debug("Initialized browser window.");
}
internal void Terminate()
{
window?.Close();
}
private void Control_AddressChanged(string address)
{
logger.Debug($"Navigated to '{address}'.");

View file

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

View file

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

View file

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

View file

@ -20,8 +20,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
public class BrowserOperationTests
{
private Mock<IActionCenter> actionCenter;
private Mock<IApplicationController> controller;
private Mock<IApplicationInfo> appInfo;
private Mock<IApplication> application;
private Mock<ILogger> logger;
private Mock<ITaskbar> taskbar;
private Mock<IUserInterfaceFactory> uiFactory;
@ -32,13 +31,12 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
public void Initialize()
{
actionCenter = new Mock<IActionCenter>();
controller = new Mock<IApplicationController>();
appInfo = new Mock<IApplicationInfo>();
application = new Mock<IApplication>();
logger = new Mock<ILogger>();
taskbar = new Mock<ITaskbar>();
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]
@ -46,7 +44,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
{
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));
actionCenter.Verify(a => a.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()
{
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 AppConfig appConfig;
public IBrowserApplicationController Browser { private get; set; }
public IBrowserApplication Browser { private get; set; }
public IClientHost ClientHost { private get; set; }
public Guid SessionId { private get; set; }
public Settings Settings { private get; set; }

View file

@ -60,7 +60,7 @@ namespace SafeExamBrowser.Client
private UserInterfaceMode uiMode;
private IActionCenter actionCenter;
private IBrowserApplicationController browserController;
private IBrowserApplication browser;
private IClientHost clientHost;
private ILogger logger;
private IMessageBox messageBox;
@ -199,11 +199,11 @@ namespace SafeExamBrowser.Client
private IOperation BuildBrowserOperation()
{
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 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;
}
@ -346,7 +346,7 @@ namespace SafeExamBrowser.Client
private void UpdateClientControllerDependencies()
{
ClientController.Browser = browserController;
ClientController.Browser = browser;
ClientController.ClientHost = clientHost;
ClientController.SessionId = configuration.SessionId;
ClientController.Settings = configuration.Settings;

View file

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

View file

@ -29,9 +29,9 @@ namespace SafeExamBrowser.UserInterface.Contracts
IWindow CreateAboutWindow(AppConfig appConfig);
/// <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>
IApplicationControl CreateApplicationControl(IApplicationInfo info, Location location);
IApplicationControl CreateApplicationControl(IApplication application, Location location);
/// <summary>
/// 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/.
*/
using SafeExamBrowser.UserInterface.Contracts.Shell.Events;
namespace SafeExamBrowser.UserInterface.Contracts.Shell
{
/// <summary>
@ -15,10 +13,5 @@ namespace SafeExamBrowser.UserInterface.Contracts.Shell
/// </summary>
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>
</UserControl.Resources>
<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">
<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" />

View file

@ -6,11 +6,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System.Windows;
using System;
using System.Windows.Controls;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell.Events;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Desktop.Controls
@ -20,7 +19,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
private IApplicationInfo info;
private IApplicationInstance instance;
internal event ApplicationControlClickedEventHandler Clicked;
internal event EventHandler Clicked;
public ActionCenterApplicationButton(IApplicationInfo info, IApplicationInstance instance = null)
{
@ -35,6 +34,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
{
Icon.Content = IconResourceLoader.Load(info.IconResource);
Text.Text = instance?.Name ?? info.Name;
Button.Click += (o, args) => Clicked?.Invoke(this, EventArgs.Empty);
Button.ToolTip = instance?.Name ?? info.Tooltip;
if (instance != null)
@ -51,16 +51,11 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
private void Instance_NameChanged(string name)
{
Dispatcher.Invoke(() =>
Dispatcher.InvokeAsync(() =>
{
Text.Text = 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.Controls;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Contracts.Shell.Events;
namespace SafeExamBrowser.UserInterface.Desktop.Controls
{
public partial class ActionCenterApplicationControl : UserControl, IApplicationControl
{
private IApplicationInfo info;
private IApplication application;
public event ApplicationControlClickedEventHandler Clicked;
public ActionCenterApplicationControl(IApplicationInfo info)
public ActionCenterApplicationControl(IApplication application)
{
this.info = info;
this.application = application;
InitializeComponent();
InitializeApplicationControl(info);
InitializeApplicationControl();
}
public void RegisterInstance(IApplicationInstance instance)
private void InitializeApplicationControl()
{
Dispatcher.Invoke(() =>
{
var button = new ActionCenterApplicationButton(info, instance);
var button = new ActionCenterApplicationButton(application.Info);
button.Clicked += (id) => Clicked?.Invoke(id);
instance.Terminated += (id) => Instance_OnTerminated(id, button);
application.InstanceStarted += Application_InstanceStarted;
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);
ApplicationName.Visibility = Visibility.Visible;
@ -44,16 +50,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
});
}
private void InitializeApplicationControl(IApplicationInfo info)
{
var button = new ActionCenterApplicationButton(info);
button.Button.Click += (o, args) => Clicked?.Invoke();
ApplicationName.Text = info.Name;
ApplicationButton.Content = button;
}
private void Instance_OnTerminated(InstanceIdentifier id, ActionCenterApplicationButton button)
private void RemoveInstance(ActionCenterApplicationButton button)
{
Dispatcher.InvokeAsync(() =>
{

View file

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

View file

@ -14,7 +14,7 @@
</ResourceDictionary>
</UserControl.Resources>
<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">
<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" />

View file

@ -10,7 +10,6 @@ using System.Windows;
using System.Windows.Controls;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell.Events;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Desktop.Controls
@ -20,8 +19,6 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
private IApplicationInfo info;
private IApplicationInstance instance;
internal event ApplicationControlClickedEventHandler Clicked;
public TaskbarApplicationInstanceButton(IApplicationInstance instance, IApplicationInfo info)
{
this.info = info;
@ -33,12 +30,17 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
private void InitializeApplicationInstanceButton()
{
Icon.Content = IconResourceLoader.Load(info.IconResource);
Text.Text = instance.Name;
Button.Click += Button_Click;
Button.ToolTip = instance.Name;
Icon.Content = IconResourceLoader.Load(info.IconResource);
instance.IconChanged += Instance_IconChanged;
instance.NameChanged += Instance_NameChanged;
Text.Text = instance.Name;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
instance.Activate();
}
private void Instance_IconChanged(IIconResource icon)
@ -54,10 +56,5 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
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);
}
public IApplicationControl CreateApplicationControl(IApplicationInfo info, Location location)
public IApplicationControl CreateApplicationControl(IApplication application, Location location)
{
if (location == Location.ActionCenter)
{
return new ActionCenterApplicationControl(info);
return new ActionCenterApplicationControl(application);
}
else
{
return new TaskbarApplicationControl(info);
return new TaskbarApplicationControl(application);
}
}

View file

@ -14,7 +14,7 @@
</ResourceDictionary>
</UserControl.Resources>
<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">
<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" />

View file

@ -6,11 +6,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System.Windows;
using System;
using System.Windows.Controls;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell.Events;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls
@ -20,7 +19,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
private IApplicationInfo info;
private IApplicationInstance instance;
internal event ApplicationControlClickedEventHandler Clicked;
internal event EventHandler Clicked;
public ActionCenterApplicationButton(IApplicationInfo info, IApplicationInstance instance = null)
{
@ -35,6 +34,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
Icon.Content = IconResourceLoader.Load(info.IconResource);
Text.Text = instance?.Name ?? info.Name;
Button.Click += (o, args) => Clicked?.Invoke(this, EventArgs.Empty);
Button.ToolTip = instance?.Name ?? info.Tooltip;
if (instance != null)
@ -51,16 +51,11 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
private void Instance_NameChanged(string name)
{
Dispatcher.Invoke(() =>
Dispatcher.InvokeAsync(() =>
{
Text.Text = 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.Controls;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Contracts.Shell.Events;
namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
public partial class ActionCenterApplicationControl : UserControl, IApplicationControl
{
private IApplicationInfo info;
private IApplication application;
public event ApplicationControlClickedEventHandler Clicked;
public ActionCenterApplicationControl(IApplicationInfo info)
public ActionCenterApplicationControl(IApplication application)
{
this.info = info;
this.application = application;
InitializeComponent();
InitializeApplicationControl(info);
InitializeApplicationControl();
}
public void RegisterInstance(IApplicationInstance instance)
private void InitializeApplicationControl()
{
Dispatcher.Invoke(() =>
{
var button = new ActionCenterApplicationButton(info, instance);
var button = new ActionCenterApplicationButton(application.Info);
button.Clicked += (id) => Clicked?.Invoke(id);
instance.Terminated += (id) => Instance_OnTerminated(id, button);
application.InstanceStarted += Application_InstanceStarted;
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);
ApplicationName.Visibility = Visibility.Visible;
@ -44,16 +50,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
});
}
private void InitializeApplicationControl(IApplicationInfo info)
{
var button = new ActionCenterApplicationButton(info);
button.Button.Click += (o, args) => Clicked?.Invoke();
ApplicationName.Text = info.Name;
ApplicationButton.Content = button;
}
private void Instance_OnTerminated(InstanceIdentifier id, ActionCenterApplicationButton button)
private void RemoveInstance(ActionCenterApplicationButton button)
{
Dispatcher.InvokeAsync(() =>
{

View file

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

View file

@ -14,7 +14,7 @@
</ResourceDictionary>
</UserControl.Resources>
<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">
<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" />

View file

@ -10,7 +10,6 @@ using System.Windows;
using System.Windows.Controls;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.Core.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell.Events;
using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls
@ -20,8 +19,6 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
private IApplicationInfo info;
private IApplicationInstance instance;
internal event ApplicationControlClickedEventHandler Clicked;
public TaskbarApplicationInstanceButton(IApplicationInstance instance, IApplicationInfo info)
{
this.info = info;
@ -33,12 +30,17 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
private void InitializeApplicationInstanceButton()
{
Icon.Content = IconResourceLoader.Load(info.IconResource);
Text.Text = instance.Name;
Button.Click += Button_Click;
Button.ToolTip = instance.Name;
Icon.Content = IconResourceLoader.Load(info.IconResource);
instance.IconChanged += Instance_IconChanged;
instance.NameChanged += Instance_NameChanged;
Text.Text = instance.Name;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
instance.Activate();
}
private void Instance_IconChanged(IIconResource icon)
@ -54,10 +56,5 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
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);
}
public IApplicationControl CreateApplicationControl(IApplicationInfo info, Location location)
public IApplicationControl CreateApplicationControl(IApplication application, Location location)
{
if (location == Location.ActionCenter)
{
return new ActionCenterApplicationControl(info);
return new ActionCenterApplicationControl(application);
}
else
{
return new TaskbarApplicationControl(info);
return new TaskbarApplicationControl(application);
}
}