diff --git a/SafeExamBrowser.Browser/BrowserApplicationController.cs b/SafeExamBrowser.Browser/BrowserApplicationController.cs index 9fbb3223..3f4ad79f 100644 --- a/SafeExamBrowser.Browser/BrowserApplicationController.cs +++ b/SafeExamBrowser.Browser/BrowserApplicationController.cs @@ -149,6 +149,7 @@ namespace SafeExamBrowser.Browser private void Instance_PopupRequested(PopupRequestedEventArgs args) { + // TODO: Use settings for additional browser windows! var popupSettings = new BrowserSettings { AllowAddressBar = false, diff --git a/SafeExamBrowser.Browser/BrowserApplicationInstance.cs b/SafeExamBrowser.Browser/BrowserApplicationInstance.cs index 8aaf52f4..42d00d6d 100644 --- a/SafeExamBrowser.Browser/BrowserApplicationInstance.cs +++ b/SafeExamBrowser.Browser/BrowserApplicationInstance.cs @@ -76,7 +76,10 @@ namespace SafeExamBrowser.Browser displayHandler.FaviconChanged += DisplayHandler_FaviconChanged; downloadHandler.ConfigurationDownloadRequested += DownloadHandler_ConfigurationDownloadRequested; - keyboardHandler.ReloadRequested += KeyboardHandler_ReloadRequested; + keyboardHandler.ReloadRequested += ReloadRequested; + keyboardHandler.ZoomInRequested += ZoomInRequested; + keyboardHandler.ZoomOutRequested += ZoomOutRequested; + keyboardHandler.ZoomResetRequested += ZoomResetRequested; lifeSpanHandler.PopupRequested += LifeSpanHandler_PopupRequested; control = new BrowserControl(contextMenuHandler, displayHandler, downloadHandler, keyboardHandler, lifeSpanHandler, requestHandler, settings.StartUrl); @@ -91,40 +94,16 @@ namespace SafeExamBrowser.Browser window.IsMainWindow = isMainInstance; window.Closing += () => Terminated?.Invoke(Id); window.AddressChanged += Window_AddressChanged; - window.ReloadRequested += Window_ReloadRequested; + window.ReloadRequested += ReloadRequested; window.BackwardNavigationRequested += Window_BackwardNavigationRequested; window.ForwardNavigationRequested += Window_ForwardNavigationRequested; + window.ZoomInRequested += ZoomInRequested; + window.ZoomOutRequested += ZoomOutRequested; + window.ZoomResetRequested += ZoomResetRequested; logger.Debug("Initialized browser window."); } - private void HandleReloadRequest() - { - if (settings.AllowReloading && settings.ShowReloadWarning) - { - var result = messageBox.Show(TextKey.MessageBox_ReloadConfirmation, TextKey.MessageBox_ReloadConfirmationTitle, MessageBoxAction.YesNo, MessageBoxIcon.Question, window); - - if (result == MessageBoxResult.Yes) - { - logger.Debug("The user confirmed reloading the current page..."); - control.Reload(); - } - else - { - logger.Debug("The user aborted reloading the current page."); - } - } - else if (settings.AllowReloading) - { - logger.Debug("Reloading current page..."); - control.Reload(); - } - else - { - logger.Debug("Blocked reload attempt, as the user is not allowed to reload web pages."); - } - } - private void Control_AddressChanged(string address) { logger.Debug($"Navigated to '{address}'."); @@ -164,11 +143,6 @@ namespace SafeExamBrowser.Browser } } - private void KeyboardHandler_ReloadRequested() - { - HandleReloadRequest(); - } - private void LifeSpanHandler_PopupRequested(PopupRequestedEventArgs args) { if (settings.AllowPopups) @@ -182,17 +156,39 @@ namespace SafeExamBrowser.Browser } } + private void ReloadRequested() + { + if (settings.AllowReloading && settings.ShowReloadWarning) + { + var result = messageBox.Show(TextKey.MessageBox_ReloadConfirmation, TextKey.MessageBox_ReloadConfirmationTitle, MessageBoxAction.YesNo, MessageBoxIcon.Question, window); + + if (result == MessageBoxResult.Yes) + { + logger.Debug("The user confirmed reloading the current page..."); + control.Reload(); + } + else + { + logger.Debug("The user aborted reloading the current page."); + } + } + else if (settings.AllowReloading) + { + logger.Debug("Reloading current page..."); + control.Reload(); + } + else + { + logger.Debug("Blocked reload attempt, as the user is not allowed to reload web pages."); + } + } + private void Window_AddressChanged(string address) { logger.Debug($"The user requested to navigate to '{address}'."); control.NavigateTo(address); } - private void Window_ReloadRequested() - { - HandleReloadRequest(); - } - private void Window_BackwardNavigationRequested() { logger.Debug($"Navigating forwards..."); @@ -204,5 +200,32 @@ namespace SafeExamBrowser.Browser logger.Debug($"Navigating backwards..."); control.NavigateForwards(); } + + private void ZoomInRequested() + { + if (settings.AllowPageZoom) + { + control.ZoomIn(); + logger.Debug("Increased page zoom."); + } + } + + private void ZoomOutRequested() + { + if (settings.AllowPageZoom) + { + control.ZoomOut(); + logger.Debug("Decreased page zoom."); + } + } + + private void ZoomResetRequested() + { + if (settings.AllowPageZoom) + { + control.ZoomReset(); + logger.Debug("Reset page zoom."); + } + } } } diff --git a/SafeExamBrowser.Browser/BrowserControl.cs b/SafeExamBrowser.Browser/BrowserControl.cs index 994ca66c..4a1d8444 100644 --- a/SafeExamBrowser.Browser/BrowserControl.cs +++ b/SafeExamBrowser.Browser/BrowserControl.cs @@ -15,6 +15,8 @@ namespace SafeExamBrowser.Browser { internal class BrowserControl : ChromiumWebBrowser, IBrowserControl { + private const double ZOOM_FACTOR = 0.1; + private IContextMenuHandler contextMenuHandler; private IDisplayHandler displayHandler; private IDownloadHandler downloadHandler; @@ -94,5 +96,32 @@ namespace SafeExamBrowser.Browser { GetBrowser().Reload(); } + + public void ZoomReset() + { + GetBrowser().SetZoomLevel(0); + } + + public void ZoomIn() + { + GetBrowser().GetZoomLevelAsync().ContinueWith(task => + { + if (task.IsCompleted) + { + GetBrowser().SetZoomLevel(task.Result + ZOOM_FACTOR); + } + }); + } + + public void ZoomOut() + { + GetBrowser().GetZoomLevelAsync().ContinueWith(task => + { + if (task.IsCompleted) + { + GetBrowser().SetZoomLevel(task.Result - ZOOM_FACTOR); + } + }); + } } } diff --git a/SafeExamBrowser.Browser/Events/ReloadRequestedEventHandler.cs b/SafeExamBrowser.Browser/Events/ReloadRequestedEventHandler.cs deleted file mode 100644 index 7b6fdb96..00000000 --- a/SafeExamBrowser.Browser/Events/ReloadRequestedEventHandler.cs +++ /dev/null @@ -1,12 +0,0 @@ -/* - * 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.Browser.Events -{ - internal delegate void ReloadRequestedEventHandler(); -} diff --git a/SafeExamBrowser.Browser/Handlers/KeyboardHandler.cs b/SafeExamBrowser.Browser/Handlers/KeyboardHandler.cs index cce9fb8d..c66778cc 100644 --- a/SafeExamBrowser.Browser/Handlers/KeyboardHandler.cs +++ b/SafeExamBrowser.Browser/Handlers/KeyboardHandler.cs @@ -8,7 +8,7 @@ using System.Windows.Forms; using CefSharp; -using SafeExamBrowser.Browser.Events; +using SafeExamBrowser.Contracts.UserInterface; namespace SafeExamBrowser.Browser.Handlers { @@ -17,16 +17,37 @@ namespace SafeExamBrowser.Browser.Handlers /// internal class KeyboardHandler : IKeyboardHandler { - public event ReloadRequestedEventHandler ReloadRequested; + public event ActionRequestedEventHandler ReloadRequested; + public event ActionRequestedEventHandler ZoomInRequested; + public event ActionRequestedEventHandler ZoomOutRequested; + public event ActionRequestedEventHandler ZoomResetRequested; - public bool OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey) + public bool OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int keyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey) { + var ctrl = modifiers.HasFlag(CefEventFlags.ControlDown); + var shift = modifiers.HasFlag(CefEventFlags.ShiftDown); + + if (type == KeyType.KeyUp && ((keyCode == (int)Keys.Add && ctrl) || (keyCode == (int)Keys.D1 && ctrl && shift))) + { + ZoomInRequested?.Invoke(); + } + + if (type == KeyType.KeyUp && (keyCode == (int) Keys.Subtract || keyCode == (int) Keys.OemMinus) && ctrl) + { + ZoomOutRequested?.Invoke(); + } + + if (type == KeyType.KeyUp && (keyCode == (int) Keys.D0 || keyCode == (int) Keys.NumPad0) && ctrl) + { + ZoomResetRequested?.Invoke(); + } + return false; } - public bool OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut) + public bool OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int keyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut) { - if (type == KeyType.KeyUp && windowsKeyCode == (int) Keys.F5) + if (type == KeyType.KeyUp && keyCode == (int) Keys.F5) { ReloadRequested?.Invoke(); diff --git a/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj b/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj index 8d509765..4eb8602b 100644 --- a/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj +++ b/SafeExamBrowser.Browser/SafeExamBrowser.Browser.csproj @@ -69,7 +69,6 @@ - Component diff --git a/SafeExamBrowser.Contracts/I18n/TextKey.cs b/SafeExamBrowser.Contracts/I18n/TextKey.cs index 72ac5cb8..0243fc5b 100644 --- a/SafeExamBrowser.Contracts/I18n/TextKey.cs +++ b/SafeExamBrowser.Contracts/I18n/TextKey.cs @@ -15,6 +15,7 @@ namespace SafeExamBrowser.Contracts.I18n public enum TextKey { Browser_ShowDeveloperConsole, + BrowserWindow_ZoomMenuItem, LogWindow_Title, MessageBox_ApplicationError, MessageBox_ApplicationErrorTitle, diff --git a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj index 0e9831e3..1b1402f2 100644 --- a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj +++ b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj @@ -174,7 +174,7 @@ - + diff --git a/SafeExamBrowser.Contracts/UserInterface/Browser/IBrowserControl.cs b/SafeExamBrowser.Contracts/UserInterface/Browser/IBrowserControl.cs index ce502faf..851ae959 100644 --- a/SafeExamBrowser.Contracts/UserInterface/Browser/IBrowserControl.cs +++ b/SafeExamBrowser.Contracts/UserInterface/Browser/IBrowserControl.cs @@ -55,5 +55,20 @@ namespace SafeExamBrowser.Contracts.UserInterface.Browser /// Reloads the current web page. /// void Reload(); + + /// + /// Increases the page zoom. + /// + void ZoomIn(); + + /// + /// Decreases the page zoom. + /// + void ZoomOut(); + + /// + /// Resets the page zoom. + /// + void ZoomReset(); } } diff --git a/SafeExamBrowser.Contracts/UserInterface/Browser/IBrowserWindow.cs b/SafeExamBrowser.Contracts/UserInterface/Browser/IBrowserWindow.cs index a155cc2b..6988bfeb 100644 --- a/SafeExamBrowser.Contracts/UserInterface/Browser/IBrowserWindow.cs +++ b/SafeExamBrowser.Contracts/UserInterface/Browser/IBrowserWindow.cs @@ -37,6 +37,21 @@ namespace SafeExamBrowser.Contracts.UserInterface.Browser /// event ActionRequestedEventHandler ReloadRequested; + /// + /// Event fired when the user would like to zoom in. + /// + event ActionRequestedEventHandler ZoomInRequested; + + /// + /// Event fired when the user would like to zoom out. + /// + event ActionRequestedEventHandler ZoomOutRequested; + + /// + /// Event fired when the user would like to reset the zoom factor. + /// + event ActionRequestedEventHandler ZoomResetRequested; + /// /// Determines whether this window is the main browser window. /// diff --git a/SafeExamBrowser.Contracts/UserInterface/Browser/Events/ActionRequestedEventHandler.cs b/SafeExamBrowser.Contracts/UserInterface/Events/ActionRequestedEventHandler.cs similarity index 87% rename from SafeExamBrowser.Contracts/UserInterface/Browser/Events/ActionRequestedEventHandler.cs rename to SafeExamBrowser.Contracts/UserInterface/Events/ActionRequestedEventHandler.cs index b0c625d7..691e7778 100644 --- a/SafeExamBrowser.Contracts/UserInterface/Browser/Events/ActionRequestedEventHandler.cs +++ b/SafeExamBrowser.Contracts/UserInterface/Events/ActionRequestedEventHandler.cs @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -namespace SafeExamBrowser.Contracts.UserInterface.Browser.Events +namespace SafeExamBrowser.Contracts.UserInterface { /// /// Indicates that the user requested an action. diff --git a/SafeExamBrowser.I18n/Text.xml b/SafeExamBrowser.I18n/Text.xml index f3a412cc..26af67ae 100644 --- a/SafeExamBrowser.I18n/Text.xml +++ b/SafeExamBrowser.I18n/Text.xml @@ -3,6 +3,9 @@ Open Console + + Page Zoom + Application Log diff --git a/SafeExamBrowser.UserInterface.Desktop/BrowserWindow.xaml b/SafeExamBrowser.UserInterface.Desktop/BrowserWindow.xaml index d5600f77..a51ac5bb 100644 --- a/SafeExamBrowser.UserInterface.Desktop/BrowserWindow.xaml +++ b/SafeExamBrowser.UserInterface.Desktop/BrowserWindow.xaml @@ -4,13 +4,14 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:fa="http://schemas.fontawesome.io/icons/" - xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop" + xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Controls" mc:Ignorable="d" Title="BrowserWindow" Background="#FFF0F0F0" Height="500" Width="750" MinHeight="250" MinWidth="250" Icon=".\Images\SafeExamBrowser.ico"> + @@ -19,21 +20,47 @@ - + + + + - - - + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Desktop/BrowserWindow.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/BrowserWindow.xaml.cs index 4c3d211e..cd29050c 100644 --- a/SafeExamBrowser.UserInterface.Desktop/BrowserWindow.xaml.cs +++ b/SafeExamBrowser.UserInterface.Desktop/BrowserWindow.xaml.cs @@ -7,11 +7,15 @@ */ using System; +using System.Threading.Tasks; using System.Windows; using System.Windows.Input; +using System.Windows.Media; using System.Windows.Media.Imaging; using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration.Settings; +using SafeExamBrowser.Contracts.I18n; +using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface.Browser; using SafeExamBrowser.Contracts.UserInterface.Browser.Events; using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events; @@ -24,7 +28,8 @@ namespace SafeExamBrowser.UserInterface.Desktop { private bool isMainWindow; private BrowserSettings settings; - public WindowClosingEventHandler closing; + private IText text; + private WindowClosingEventHandler closing; public bool IsMainWindow { @@ -43,6 +48,9 @@ namespace SafeExamBrowser.UserInterface.Desktop public event ActionRequestedEventHandler BackwardNavigationRequested; public event ActionRequestedEventHandler ForwardNavigationRequested; public event ActionRequestedEventHandler ReloadRequested; + public event ActionRequestedEventHandler ZoomInRequested; + public event ActionRequestedEventHandler ZoomOutRequested; + public event ActionRequestedEventHandler ZoomResetRequested; event WindowClosingEventHandler IWindow.Closing { @@ -50,9 +58,10 @@ namespace SafeExamBrowser.UserInterface.Desktop remove { closing -= value; } } - public BrowserWindow(IBrowserControl browserControl, BrowserSettings settings) + public BrowserWindow(IBrowserControl browserControl, BrowserSettings settings, IText text) { this.settings = settings; + this.text = text; InitializeComponent(); InitializeBrowserWindow(browserControl); @@ -112,25 +121,36 @@ namespace SafeExamBrowser.UserInterface.Desktop private void InitializeBrowserWindow(IBrowserControl browserControl) { + var originalBrush = MenuButton.Background; + if (browserControl is System.Windows.Forms.Control control) { BrowserControlHost.Child = control; } + BackButton.Click += (o, args) => BackwardNavigationRequested?.Invoke(); Closing += (o, args) => closing?.Invoke(); + ForwardButton.Click += (o, args) => ForwardNavigationRequested?.Invoke(); + MenuButton.Click += (o, args) => MenuPopup.IsOpen = !MenuPopup.IsOpen; + MenuButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = MenuPopup.IsMouseOver)); + MenuPopup.Closed += (o, args) => { MenuButton.Background = originalBrush; }; + MenuPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = IsMouseOver)); + MenuPopup.Opened += (o, args) => { MenuButton.Background = Brushes.LightGray; }; KeyUp += BrowserWindow_KeyUp; + ReloadButton.Click += (o, args) => ReloadRequested?.Invoke(); UrlTextBox.GotKeyboardFocus += (o, args) => UrlTextBox.SelectAll(); UrlTextBox.GotMouseCapture += UrlTextBox_GotMouseCapture; UrlTextBox.LostKeyboardFocus += (o, args) => UrlTextBox.Tag = null; UrlTextBox.LostFocus += (o, args) => UrlTextBox.Tag = null; UrlTextBox.KeyUp += UrlTextBox_KeyUp; UrlTextBox.MouseDoubleClick += (o, args) => UrlTextBox.SelectAll(); - ReloadButton.Click += (o, args) => ReloadRequested?.Invoke(); - BackButton.Click += (o, args) => BackwardNavigationRequested?.Invoke(); - ForwardButton.Click += (o, args) => ForwardNavigationRequested?.Invoke(); + ZoomInButton.Click += (o, args) => ZoomInRequested?.Invoke(); + ZoomOutButton.Click += (o, args) => ZoomOutRequested?.Invoke(); + ZoomResetButton.Click += (o, args) => ZoomResetRequested?.Invoke(); ApplySettings(); LoadIcons(); + LoadText(); } private void UrlTextBox_GotMouseCapture(object sender, MouseEventArgs e) @@ -198,14 +218,22 @@ namespace SafeExamBrowser.UserInterface.Desktop { var backUri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/NavigateBack.xaml"); var forwardUri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/NavigateForward.xaml"); + var menuUri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Menu.xaml"); var reloadUri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Reload.xaml"); var back = new XamlIconResource(backUri); var forward = new XamlIconResource(forwardUri); + var menu = new XamlIconResource(menuUri); var reload = new XamlIconResource(reloadUri); - ReloadButton.Content = IconResourceLoader.Load(reload); BackButton.Content = IconResourceLoader.Load(back); ForwardButton.Content = IconResourceLoader.Load(forward); + MenuButton.Content = IconResourceLoader.Load(menu); + ReloadButton.Content = IconResourceLoader.Load(reload); + } + + private void LoadText() + { + ZoomText.Text = text.Get(TextKey.BrowserWindow_ZoomMenuItem); } } } diff --git a/SafeExamBrowser.UserInterface.Desktop/Images/Menu.xaml b/SafeExamBrowser.UserInterface.Desktop/Images/Menu.xaml new file mode 100644 index 00000000..f9188146 --- /dev/null +++ b/SafeExamBrowser.UserInterface.Desktop/Images/Menu.xaml @@ -0,0 +1,7 @@ + + + + + diff --git a/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj b/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj index d119957b..fba1440e 100644 --- a/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj +++ b/SafeExamBrowser.UserInterface.Desktop/SafeExamBrowser.UserInterface.Desktop.csproj @@ -162,6 +162,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile diff --git a/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs index 43c4142d..1e872eff 100644 --- a/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs +++ b/SafeExamBrowser.UserInterface.Desktop/UserInterfaceFactory.cs @@ -45,7 +45,7 @@ namespace SafeExamBrowser.UserInterface.Desktop public IBrowserWindow CreateBrowserWindow(IBrowserControl control, BrowserSettings settings) { - return new BrowserWindow(control, settings); + return new BrowserWindow(control, settings, text); } public ISystemKeyboardLayoutControl CreateKeyboardLayoutControl()