SEBWIN-106: Implemented basic zoom functionality for browser.

This commit is contained in:
dbuechel 2019-01-18 16:11:33 +01:00
parent 483eec6ef8
commit 6527969e75
17 changed files with 235 additions and 74 deletions

View file

@ -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,

View file

@ -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.");
}
}
}
}

View file

@ -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);
}
});
}
}
}

View file

@ -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();
}

View file

@ -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
/// </remarks>
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();

View file

@ -69,7 +69,6 @@
<Compile Include="Events\FaviconChangedEventHandler.cs" />
<Compile Include="Events\PopupRequestedEventArgs.cs" />
<Compile Include="Events\PopupRequestedEventHandler.cs" />
<Compile Include="Events\ReloadRequestedEventHandler.cs" />
<Compile Include="Handlers\ContextMenuHandler.cs" />
<Compile Include="BrowserControl.cs">
<SubType>Component</SubType>

View file

@ -15,6 +15,7 @@ namespace SafeExamBrowser.Contracts.I18n
public enum TextKey
{
Browser_ShowDeveloperConsole,
BrowserWindow_ZoomMenuItem,
LogWindow_Title,
MessageBox_ApplicationError,
MessageBox_ApplicationErrorTitle,

View file

@ -174,7 +174,7 @@
<Compile Include="SystemComponents\ISystemComponent.cs" />
<Compile Include="SystemComponents\IWirelessNetwork.cs" />
<Compile Include="SystemComponents\WirelessNetworkStatus.cs" />
<Compile Include="UserInterface\Browser\Events\ActionRequestedEventHandler.cs" />
<Compile Include="UserInterface\Events\ActionRequestedEventHandler.cs" />
<Compile Include="UserInterface\Browser\Events\AddressChangedEventHandler.cs" />
<Compile Include="UserInterface\Browser\Events\LoadingStateChangedEventHandler.cs" />
<Compile Include="UserInterface\Browser\Events\TitleChangedEventHandler.cs" />

View file

@ -55,5 +55,20 @@ namespace SafeExamBrowser.Contracts.UserInterface.Browser
/// Reloads the current web page.
/// </summary>
void Reload();
/// <summary>
/// Increases the page zoom.
/// </summary>
void ZoomIn();
/// <summary>
/// Decreases the page zoom.
/// </summary>
void ZoomOut();
/// <summary>
/// Resets the page zoom.
/// </summary>
void ZoomReset();
}
}

View file

@ -37,6 +37,21 @@ namespace SafeExamBrowser.Contracts.UserInterface.Browser
/// </summary>
event ActionRequestedEventHandler ReloadRequested;
/// <summary>
/// Event fired when the user would like to zoom in.
/// </summary>
event ActionRequestedEventHandler ZoomInRequested;
/// <summary>
/// Event fired when the user would like to zoom out.
/// </summary>
event ActionRequestedEventHandler ZoomOutRequested;
/// <summary>
/// Event fired when the user would like to reset the zoom factor.
/// </summary>
event ActionRequestedEventHandler ZoomResetRequested;
/// <summary>
/// Determines whether this window is the main browser window.
/// </summary>

View file

@ -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
{
/// <summary>
/// Indicates that the user requested an action.

View file

@ -3,6 +3,9 @@
<Entry key="Browser_ShowDeveloperConsole">
Open Console
</Entry>
<Entry key="BrowserWindow_ZoomMenuItem">
Page Zoom
</Entry>
<Entry key="LogWindow_Title">
Application Log
</Entry>

View file

@ -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">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="./Templates/Buttons.xaml" />
<ResourceDictionary Source="./Templates/Colors.xaml" />
<ResourceDictionary Source="./Templates/ScrollViewers.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
@ -19,21 +20,47 @@
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Grid.Row="0" BorderBrush="LightGray" BorderThickness="0 0 0 1">
<Border Grid.Row="0" BorderBrush="LightGray" BorderThickness="0,0,0,1" Margin="5,0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" Height="25" Margin="10">
<Button Grid.Column="0" x:Name="BackButton" Height="30" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
<Button Grid.Column="1" x:Name="ForwardButton" Height="30" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
<Button Grid.Column="2" x:Name="ReloadButton" Height="30" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
<Grid Grid.Column="3" Height="25" Margin="5,0">
<TextBox x:Name="UrlTextBox" HorizontalAlignment="Stretch" Padding="5,0" VerticalContentAlignment="Center" />
<fa:ImageAwesome x:Name="LoadingIcon" Foreground="Gray" HorizontalAlignment="Right" Icon="Spinner" Margin="5,3" SpinDuration="1.5" Visibility="Collapsed" />
</Grid>
<Button Grid.Column="1" x:Name="ReloadButton" Height="30" HorizontalAlignment="Center" Margin="2.5,5,2.5,5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
<Button Grid.Column="2" x:Name="BackButton" Height="30" HorizontalAlignment="Center" Margin="2.5,5,2.5,5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
<Button Grid.Column="3" x:Name="ForwardButton" Height="30" HorizontalAlignment="Center" Margin="2.5,5,5,5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
<Button Grid.Column="4" x:Name="MenuButton" Height="30" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
<Popup x:Name="MenuPopup" IsOpen="False" Placement="Bottom" PlacementTarget="{Binding ElementName=MenuButton}" HorizontalOffset="-215" VerticalOffset="4">
<Border Background="{StaticResource BackgroundBrush}" BorderBrush="LightGray" BorderThickness="0.75,0,0.75,0.75" Width="250">
<StackPanel Orientation="Vertical">
<Grid Height="40">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" x:Name="ZoomText" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Button Grid.Column="1" x:Name="ZoomInButton" Margin="5" Padding="5" Template="{StaticResource BrowserButton}">
<fa:ImageAwesome Icon="SearchPlus" />
</Button>
<Button Grid.Column="2" x:Name="ZoomResetButton" Margin="5" Padding="5" Template="{StaticResource BrowserButton}">
<fa:ImageAwesome Icon="Refresh" />
</Button>
<Button Grid.Column="3" x:Name="ZoomOutButton" Margin="5" Padding="5" Template="{StaticResource BrowserButton}">
<fa:ImageAwesome Icon="SearchMinus" />
</Button>
</Grid>
</StackPanel>
</Border>
</Popup>
</Grid>
</Border>
<WindowsFormsHost Grid.Row="1" x:Name="BrowserControlHost" />

View file

@ -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);
}
}
}

View file

@ -0,0 +1,7 @@
<Viewbox Stretch="Uniform"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:fa="http://schemas.fontawesome.io/icons/">
<Canvas Height="120" Width="120">
<Path Stroke="Black" StrokeLineJoin="Round" StrokeStartLineCap="Round" StrokeEndLineCap="Round" StrokeThickness="8" Data="M 20,30 L 100,30 M 20,60 L 100,60 M 20,90 L 100,90" />
</Canvas>
</Viewbox>

View file

@ -162,6 +162,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Resource Include="Images\Menu.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Resource>
<Page Include="Templates\ScrollViewers.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>

View file

@ -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()