From e623101f8cede66ca4228f858e771d96bf1c61d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damian=20B=C3=BCchel?= Date: Fri, 14 Jul 2017 10:28:59 +0200 Subject: [PATCH] Still working on the taskbar. Implemented basic version of taskbar buttons with instance popup and made splash screen localizable. --- .../BrowserApplicationController.cs | 30 ++++++++ .../BrowserApplicationInstance.cs | 25 +++++++ .../SafeExamBrowser.Browser.csproj | 2 + .../Behaviour/IApplicationController.cs | 4 +- .../Configuration/IApplicationInstance.cs | 25 +++++++ .../Configuration/ISettings.cs | 16 ++-- SafeExamBrowser.Contracts/I18n/Key.cs | 5 +- .../SafeExamBrowser.Contracts.csproj | 3 +- ...TaskbarButton.cs => IApplicationButton.cs} | 10 +-- .../UserInterface/ISplashScreen.cs | 9 ++- .../UserInterface/ITaskbar.cs | 2 +- .../UserInterface/IUiElementFactory.cs | 2 +- .../Behaviour/StartupController.cs | 54 ++++++++++---- .../Configuration/Settings.cs | 28 +++---- SafeExamBrowser.Core/I18n/Text.xml | 3 + .../Controls/ApplicationButton.xaml | 35 +++++++++ .../Controls/ApplicationButton.xaml.cs | 74 +++++++++++++++++++ ...on.xaml => ApplicationInstanceButton.xaml} | 7 +- .../ApplicationInstanceButton.xaml.cs | 51 +++++++++++++ .../Controls/TaskbarButton.xaml.cs | 72 ------------------ .../SafeExamBrowser.UserInterface.csproj | 14 +++- .../SplashScreen.xaml.cs | 30 ++++---- SafeExamBrowser.UserInterface/Taskbar.xaml.cs | 2 +- .../UiElementFactory.cs | 4 +- .../ApplicationIconResourceLoader.cs | 46 ++++++++++++ SafeExamBrowser/App.cs | 8 +- SafeExamBrowser/CompositionRoot.cs | 23 +++--- 27 files changed, 421 insertions(+), 163 deletions(-) create mode 100644 SafeExamBrowser.Browser/BrowserApplicationController.cs create mode 100644 SafeExamBrowser.Browser/BrowserApplicationInstance.cs create mode 100644 SafeExamBrowser.Contracts/Configuration/IApplicationInstance.cs rename SafeExamBrowser.Contracts/UserInterface/{ITaskbarButton.cs => IApplicationButton.cs} (77%) create mode 100644 SafeExamBrowser.UserInterface/Controls/ApplicationButton.xaml create mode 100644 SafeExamBrowser.UserInterface/Controls/ApplicationButton.xaml.cs rename SafeExamBrowser.UserInterface/Controls/{TaskbarButton.xaml => ApplicationInstanceButton.xaml} (92%) create mode 100644 SafeExamBrowser.UserInterface/Controls/ApplicationInstanceButton.xaml.cs delete mode 100644 SafeExamBrowser.UserInterface/Controls/TaskbarButton.xaml.cs create mode 100644 SafeExamBrowser.UserInterface/Utilities/ApplicationIconResourceLoader.cs diff --git a/SafeExamBrowser.Browser/BrowserApplicationController.cs b/SafeExamBrowser.Browser/BrowserApplicationController.cs new file mode 100644 index 00000000..8556c8b9 --- /dev/null +++ b/SafeExamBrowser.Browser/BrowserApplicationController.cs @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using System; +using SafeExamBrowser.Contracts.Behaviour; +using SafeExamBrowser.Contracts.UserInterface; + +namespace SafeExamBrowser.Browser +{ + public class BrowserApplicationController : IApplicationController + { + private IApplicationButton button; + + public void RegisterApplicationButton(IApplicationButton button) + { + this.button = button; + this.button.OnClick += ButtonClick; + } + + private void ButtonClick(Guid? instanceId = null) + { + button.RegisterInstance(new BrowserApplicationInstance("A new instance. Yaji...")); + } + } +} diff --git a/SafeExamBrowser.Browser/BrowserApplicationInstance.cs b/SafeExamBrowser.Browser/BrowserApplicationInstance.cs new file mode 100644 index 00000000..fda9df52 --- /dev/null +++ b/SafeExamBrowser.Browser/BrowserApplicationInstance.cs @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using System; +using SafeExamBrowser.Contracts.Configuration; + +namespace SafeExamBrowser.Browser +{ + public class BrowserApplicationInstance : IApplicationInstance + { + public Guid Id { get; private set; } + public string Name { get; private set; } + + public BrowserApplicationInstance(string name) + { + Id = Guid.NewGuid(); + Name = name; + } + } +} diff --git a/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj b/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj index 31048534..c1edbf8f 100644 --- a/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj +++ b/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj @@ -40,7 +40,9 @@ + + diff --git a/SafeExamBrowser.Contracts/Behaviour/IApplicationController.cs b/SafeExamBrowser.Contracts/Behaviour/IApplicationController.cs index b3f04f7c..b57dd74a 100644 --- a/SafeExamBrowser.Contracts/Behaviour/IApplicationController.cs +++ b/SafeExamBrowser.Contracts/Behaviour/IApplicationController.cs @@ -13,8 +13,8 @@ namespace SafeExamBrowser.Contracts.Behaviour public interface IApplicationController { /// - /// The handler to be executed when an application's taskbar button gets clicked. + /// Registers the taskbar button for this application. /// - TaskbarButtonClickHandler OnClick { get; } + void RegisterApplicationButton(IApplicationButton button); } } diff --git a/SafeExamBrowser.Contracts/Configuration/IApplicationInstance.cs b/SafeExamBrowser.Contracts/Configuration/IApplicationInstance.cs new file mode 100644 index 00000000..651a3bdd --- /dev/null +++ b/SafeExamBrowser.Contracts/Configuration/IApplicationInstance.cs @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using System; + +namespace SafeExamBrowser.Contracts.Configuration +{ + public interface IApplicationInstance + { + /// + /// The unique identifier for the application instance. + /// + Guid Id { get; } + + /// + /// The name or (document) title of the application instance. + /// + string Name { get; } + } +} diff --git a/SafeExamBrowser.Contracts/Configuration/ISettings.cs b/SafeExamBrowser.Contracts/Configuration/ISettings.cs index 6a49de0b..0b618c5b 100644 --- a/SafeExamBrowser.Contracts/Configuration/ISettings.cs +++ b/SafeExamBrowser.Contracts/Configuration/ISettings.cs @@ -10,23 +10,23 @@ namespace SafeExamBrowser.Contracts.Configuration { public interface ISettings { - /// - /// The copyright information for the application, to be displayed in e.g. the log or the splash screen. - /// - string CopyrightInfo { get; } - /// /// The path where the log files are to be stored. /// string LogFolderPath { get; } /// - /// The information to be printed at the beginning of the application log. + /// The copyright information for the application (i.e. the executing assembly). /// - string LogHeader { get; } + string ProgramCopyright { get; } /// - /// The program version of the application. + /// The program title of the application (i.e. the executing assembly). + /// + string ProgramTitle { get; } + + /// + /// The program version of the application (i.e. the executing assembly). /// string ProgramVersion { get; } } diff --git a/SafeExamBrowser.Contracts/I18n/Key.cs b/SafeExamBrowser.Contracts/I18n/Key.cs index 046515a8..3ef9f78c 100644 --- a/SafeExamBrowser.Contracts/I18n/Key.cs +++ b/SafeExamBrowser.Contracts/I18n/Key.cs @@ -18,6 +18,9 @@ namespace SafeExamBrowser.Contracts.I18n MessageBox_SingleInstance, MessageBox_SingleInstanceTitle, MessageBox_StartupError, - MessageBox_StartupErrorTitle + MessageBox_StartupErrorTitle, + SplashScreen_InitializeBrowser, + SplashScreen_StartupProcedure, + Version } } diff --git a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj index 1e4bce48..61eeb7a4 100644 --- a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj +++ b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj @@ -43,6 +43,7 @@ + @@ -60,7 +61,7 @@ - + diff --git a/SafeExamBrowser.Contracts/UserInterface/ITaskbarButton.cs b/SafeExamBrowser.Contracts/UserInterface/IApplicationButton.cs similarity index 77% rename from SafeExamBrowser.Contracts/UserInterface/ITaskbarButton.cs rename to SafeExamBrowser.Contracts/UserInterface/IApplicationButton.cs index 5e4859d4..c79403b8 100644 --- a/SafeExamBrowser.Contracts/UserInterface/ITaskbarButton.cs +++ b/SafeExamBrowser.Contracts/UserInterface/IApplicationButton.cs @@ -7,12 +7,13 @@ */ using System; +using SafeExamBrowser.Contracts.Configuration; namespace SafeExamBrowser.Contracts.UserInterface { public delegate void TaskbarButtonClickHandler(Guid? instanceId = null); - public interface ITaskbarButton + public interface IApplicationButton { /// /// OnClick handler, executed when the user clicks on the application button. If multiple instances of @@ -21,11 +22,10 @@ namespace SafeExamBrowser.Contracts.UserInterface event TaskbarButtonClickHandler OnClick; /// - /// Registers a new instance of an application, to be displayed when the user clicks the taskbar button. + /// Registers a new instance of an application, to be displayed if the user clicks the taskbar button + /// when there are already one or more instances of the same application running. /// - /// The identifier for the application instance. - /// An optional title to be displayed (if multiple instances are active). - void RegisterInstance(Guid id, string title = null); + void RegisterInstance(IApplicationInstance instance); /// /// Unregisters an application instance, e.g. if it gets closed. diff --git a/SafeExamBrowser.Contracts/UserInterface/ISplashScreen.cs b/SafeExamBrowser.Contracts/UserInterface/ISplashScreen.cs index c6696ec7..8f72e6a8 100644 --- a/SafeExamBrowser.Contracts/UserInterface/ISplashScreen.cs +++ b/SafeExamBrowser.Contracts/UserInterface/ISplashScreen.cs @@ -6,11 +6,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -using SafeExamBrowser.Contracts.Logging; +using SafeExamBrowser.Contracts.I18n; namespace SafeExamBrowser.Contracts.UserInterface { - public interface ISplashScreen : ILogObserver + public interface ISplashScreen { /// /// Closes the splash screen. @@ -31,5 +31,10 @@ namespace SafeExamBrowser.Contracts.UserInterface /// Updates the progress bar of the splash screen according to the specified amount. /// void UpdateProgress(int amount = 1); + + /// + /// Updates the status text of the splash screen. + /// + void UpdateText(Key key); } } diff --git a/SafeExamBrowser.Contracts/UserInterface/ITaskbar.cs b/SafeExamBrowser.Contracts/UserInterface/ITaskbar.cs index 709a0883..dc79083a 100644 --- a/SafeExamBrowser.Contracts/UserInterface/ITaskbar.cs +++ b/SafeExamBrowser.Contracts/UserInterface/ITaskbar.cs @@ -13,7 +13,7 @@ namespace SafeExamBrowser.Contracts.UserInterface /// /// Adds the given application button to the taskbar. /// - void AddButton(ITaskbarButton button); + void AddButton(IApplicationButton button); /// /// Moves the taskbar to the given location on the screen. diff --git a/SafeExamBrowser.Contracts/UserInterface/IUiElementFactory.cs b/SafeExamBrowser.Contracts/UserInterface/IUiElementFactory.cs index f397772f..6931871f 100644 --- a/SafeExamBrowser.Contracts/UserInterface/IUiElementFactory.cs +++ b/SafeExamBrowser.Contracts/UserInterface/IUiElementFactory.cs @@ -15,6 +15,6 @@ namespace SafeExamBrowser.Contracts.UserInterface /// /// Creates a taskbar button, initialized with the given application information. /// - ITaskbarButton CreateButton(IApplicationInfo info); + IApplicationButton CreateApplicationButton(IApplicationInfo info); } } diff --git a/SafeExamBrowser.Core/Behaviour/StartupController.cs b/SafeExamBrowser.Core/Behaviour/StartupController.cs index 58ee5534..f25bfaab 100644 --- a/SafeExamBrowser.Core/Behaviour/StartupController.cs +++ b/SafeExamBrowser.Core/Behaviour/StartupController.cs @@ -20,6 +20,7 @@ namespace SafeExamBrowser.Core.Behaviour { public class StartupController : IStartupController { + private IApplicationController browserController; private IApplicationInfo browserInfo; private ILogger logger; private IMessageBox messageBox; @@ -33,7 +34,6 @@ namespace SafeExamBrowser.Core.Behaviour { get { - yield return InitializeApplicationLog; yield return HandleCommandLineArguments; yield return DetectOperatingSystem; yield return EstablishWcfServiceConnection; @@ -46,26 +46,38 @@ namespace SafeExamBrowser.Core.Behaviour } } - public StartupController(IApplicationInfo browserInfo, ILogger logger, IMessageBox messageBox, ISettings settings, ISplashScreen splashScreen, ITaskbar taskbar, IText text, IUiElementFactory uiFactory) + public StartupController( + IApplicationController browserController, + IApplicationInfo browserInfo, + ILogger logger, + IMessageBox messageBox, + ISettings settings, + ISplashScreen splashScreen, + ITaskbar taskbar, + IText text, + IUiElementFactory uiFactory) { - this.browserInfo = browserInfo; - this.logger = logger; - this.messageBox = messageBox; - this.settings = settings; - this.splashScreen = splashScreen; - this.taskbar = taskbar; - this.text = text; - this.uiFactory = uiFactory; + this.browserController = browserController ?? throw new ArgumentNullException(nameof(browserController)); + this.browserInfo = browserInfo ?? throw new ArgumentNullException(nameof(browserInfo)); + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); ; + this.messageBox = messageBox ?? throw new ArgumentNullException(nameof(messageBox)); ; + this.settings = settings ?? throw new ArgumentNullException(nameof(settings)); ; + this.splashScreen = splashScreen ?? throw new ArgumentNullException(nameof(splashScreen)); ; + this.taskbar = taskbar ?? throw new ArgumentNullException(nameof(taskbar)); ; + this.text = text ?? throw new ArgumentNullException(nameof(text)); ; + this.uiFactory = uiFactory ?? throw new ArgumentNullException(nameof(uiFactory)); ; } public bool TryInitializeApplication() { try { + InitializeApplicationLog(); + InitializeSplashScreen(); + foreach (var operation in StartupOperations) { operation(); - splashScreen.UpdateProgress(); // TODO: Remove! @@ -85,12 +97,20 @@ namespace SafeExamBrowser.Core.Behaviour private void InitializeApplicationLog() { - logger.Log(settings.LogHeader); + var titleLine = $"/* {settings.ProgramTitle}, Version {settings.ProgramVersion}{Environment.NewLine}"; + var copyrightLine = $"/* {settings.ProgramCopyright}{Environment.NewLine}"; + var emptyLine = $"/* {Environment.NewLine}"; + var githubLine = $"/* Please visit https://github.com/SafeExamBrowser for more information."; + + logger.Log($"{titleLine}{copyrightLine}{emptyLine}{githubLine}"); logger.Log($"{Environment.NewLine}# Application started at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}{Environment.NewLine}"); logger.Info("Initiating startup procedure."); - logger.Subscribe(splashScreen); + } + private void InitializeSplashScreen() + { splashScreen.SetMaxProgress(StartupOperations.Count()); + splashScreen.UpdateText(Key.SplashScreen_StartupProcedure); } private void HandleCommandLineArguments() @@ -151,9 +171,12 @@ namespace SafeExamBrowser.Core.Behaviour private void InitializeBrowser() { - logger.Info("Initializing browser."); + var browserButton = uiFactory.CreateApplicationButton(browserInfo); - var browserButton = uiFactory.CreateButton(browserInfo); + logger.Info("Initializing browser."); + splashScreen.UpdateText(Key.SplashScreen_InitializeBrowser); + + browserController.RegisterApplicationButton(browserButton); // TODO @@ -163,7 +186,6 @@ namespace SafeExamBrowser.Core.Behaviour private void FinishInitialization() { logger.Info("Application successfully initialized!"); - logger.Unsubscribe(splashScreen); } } } diff --git a/SafeExamBrowser.Core/Configuration/Settings.cs b/SafeExamBrowser.Core/Configuration/Settings.cs index b55e8a76..56b5ae25 100644 --- a/SafeExamBrowser.Core/Configuration/Settings.cs +++ b/SafeExamBrowser.Core/Configuration/Settings.cs @@ -15,7 +15,15 @@ namespace SafeExamBrowser.Core.Configuration { public class Settings : ISettings { - public string CopyrightInfo + public string LogFolderPath + { + get + { + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "SafeExamBrowser", "Logs"); + } + } + + public string ProgramCopyright { get { @@ -26,28 +34,14 @@ namespace SafeExamBrowser.Core.Configuration } } - public string LogFolderPath + public string ProgramTitle { get { - return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "SafeExamBrowser", "Logs"); - } - } - - public string LogHeader - { - get - { - var newline = Environment.NewLine; var executable = Assembly.GetEntryAssembly(); var title = executable.GetCustomAttribute().Title; - var titleLine = $"/* {title}, Version {ProgramVersion}{newline}"; - var copyrightLine = $"/* {CopyrightInfo}{newline}"; - var emptyLine = $"/* {newline}"; - var githubLine = $"/* Please visit https://github.com/SafeExamBrowser for more information."; - - return $"{titleLine}{copyrightLine}{emptyLine}{githubLine}"; + return title; } } diff --git a/SafeExamBrowser.Core/I18n/Text.xml b/SafeExamBrowser.Core/I18n/Text.xml index bd495c9e..ec535e8f 100644 --- a/SafeExamBrowser.Core/I18n/Text.xml +++ b/SafeExamBrowser.Core/I18n/Text.xml @@ -4,4 +4,7 @@ Shutdown Error An unexpected error occurred during the startup procedure! Please consult the application log for more information... Startup Error + Initializing browser. + Initiating startup procedure. + Version \ No newline at end of file diff --git a/SafeExamBrowser.UserInterface/Controls/ApplicationButton.xaml b/SafeExamBrowser.UserInterface/Controls/ApplicationButton.xaml new file mode 100644 index 00000000..57c6041d --- /dev/null +++ b/SafeExamBrowser.UserInterface/Controls/ApplicationButton.xaml @@ -0,0 +1,35 @@ + + + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface/Controls/ApplicationButton.xaml.cs b/SafeExamBrowser.UserInterface/Controls/ApplicationButton.xaml.cs new file mode 100644 index 00000000..5803c6a6 --- /dev/null +++ b/SafeExamBrowser.UserInterface/Controls/ApplicationButton.xaml.cs @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2017 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using System; +using System.Collections.Generic; +using System.Windows; +using System.Windows.Controls; +using SafeExamBrowser.Contracts.Configuration; +using SafeExamBrowser.Contracts.UserInterface; +using SafeExamBrowser.UserInterface.Utilities; + +namespace SafeExamBrowser.UserInterface.Controls +{ + public partial class ApplicationButton : UserControl, IApplicationButton + { + private IApplicationInfo info; + private IList instances = new List(); + + public event TaskbarButtonClickHandler OnClick; + + public ApplicationButton(IApplicationInfo info) + { + this.info = info; + + InitializeComponent(); + InitializeApplicationButton(); + } + + public void RegisterInstance(IApplicationInstance instance) + { + var instanceButton = new ApplicationInstanceButton(instance, info); + + instances.Add(instance); + instanceButton.Click += (id) => OnClick?.Invoke(id); + InstanceStackPanel.Children.Add(instanceButton); + + if (instances.Count > 1) + { + InstancePopup.IsOpen = true; + } + } + + public void UnregisterInstance(Guid id) + { + throw new NotImplementedException(); + } + + private void InitializeApplicationButton() + { + Button.ToolTip = info.Tooltip; + Button.MouseLeave += (o, args) => InstancePopup.IsOpen = InstancePopup.IsMouseOver; + Button.Content = ApplicationIconResourceLoader.Load(info.IconResource); + + InstancePopup.MouseLeave += (o, args) => InstancePopup.IsOpen = false; + } + + private void Button_Click(object sender, RoutedEventArgs e) + { + if (instances.Count <= 1) + { + OnClick?.Invoke(); + } + else + { + InstancePopup.IsOpen = true; + } + } + } +} diff --git a/SafeExamBrowser.UserInterface/Controls/TaskbarButton.xaml b/SafeExamBrowser.UserInterface/Controls/ApplicationInstanceButton.xaml similarity index 92% rename from SafeExamBrowser.UserInterface/Controls/TaskbarButton.xaml rename to SafeExamBrowser.UserInterface/Controls/ApplicationInstanceButton.xaml index b6ee6bc7..fe5bcc08 100644 --- a/SafeExamBrowser.UserInterface/Controls/TaskbarButton.xaml +++ b/SafeExamBrowser.UserInterface/Controls/ApplicationInstanceButton.xaml @@ -1,13 +1,12 @@ - + mc:Ignorable="d" d:DesignWidth="250"> -