SEBWIN-106: Implemented basic popup and reload handling, revised browser control implementation and added mouse button interception for navigation (auxiliary) keys. Also finally implemented a custom template for small scrollbars in scrollviewers.
This commit is contained in:
parent
91c2417930
commit
f949a19f32
40 changed files with 454 additions and 158 deletions
|
@ -11,12 +11,14 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using CefSharp;
|
||||
using CefSharp.WinForms;
|
||||
using SafeExamBrowser.Browser.Events;
|
||||
using SafeExamBrowser.Contracts.Browser;
|
||||
using SafeExamBrowser.Contracts.Configuration;
|
||||
using SafeExamBrowser.Contracts.Core;
|
||||
using SafeExamBrowser.Contracts.I18n;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
using SafeExamBrowser.Contracts.UserInterface;
|
||||
using SafeExamBrowser.Contracts.UserInterface.MessageBox;
|
||||
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
|
||||
using BrowserSettings = SafeExamBrowser.Contracts.Configuration.Settings.BrowserSettings;
|
||||
|
||||
|
@ -29,6 +31,7 @@ namespace SafeExamBrowser.Browser
|
|||
private AppConfig appConfig;
|
||||
private IApplicationButton button;
|
||||
private IList<IApplicationInstance> instances;
|
||||
private IMessageBox messageBox;
|
||||
private IModuleLogger logger;
|
||||
private BrowserSettings settings;
|
||||
private IText text;
|
||||
|
@ -39,6 +42,7 @@ namespace SafeExamBrowser.Browser
|
|||
public BrowserApplicationController(
|
||||
AppConfig appConfig,
|
||||
BrowserSettings settings,
|
||||
IMessageBox messageBox,
|
||||
IModuleLogger logger,
|
||||
IText text,
|
||||
IUserInterfaceFactory uiFactory)
|
||||
|
@ -46,6 +50,7 @@ namespace SafeExamBrowser.Browser
|
|||
this.appConfig = appConfig;
|
||||
this.instances = new List<IApplicationInstance>();
|
||||
this.logger = logger;
|
||||
this.messageBox = messageBox;
|
||||
this.settings = settings;
|
||||
this.text = text;
|
||||
this.uiFactory = uiFactory;
|
||||
|
@ -56,7 +61,7 @@ namespace SafeExamBrowser.Browser
|
|||
var cefSettings = InitializeCefSettings();
|
||||
var success = Cef.Initialize(cefSettings, true, null);
|
||||
|
||||
logger.Info("Initialized CEF.");
|
||||
logger.Info("Initialized browser engine.");
|
||||
|
||||
if (!success)
|
||||
{
|
||||
|
@ -87,18 +92,20 @@ namespace SafeExamBrowser.Browser
|
|||
|
||||
Cef.Shutdown();
|
||||
|
||||
logger.Info("Terminated CEF.");
|
||||
logger.Info("Terminated browser engine.");
|
||||
}
|
||||
|
||||
private void CreateNewInstance()
|
||||
private void CreateNewInstance(BrowserSettings custom = null)
|
||||
{
|
||||
var id = new BrowserInstanceIdentifier(++instanceIdCounter);
|
||||
var isMainInstance = instances.Count == 0;
|
||||
var instanceLogger = logger.CloneFor($"BrowserInstance {id}");
|
||||
var instance = new BrowserApplicationInstance(appConfig, settings, id, isMainInstance, instanceLogger, text, uiFactory);
|
||||
var instanceSettings = custom ?? settings;
|
||||
var instance = new BrowserApplicationInstance(appConfig, instanceSettings, id, isMainInstance, messageBox, instanceLogger, text, uiFactory);
|
||||
|
||||
instance.Initialize();
|
||||
instance.ConfigurationDownloadRequested += (fileName, args) => ConfigurationDownloadRequested?.Invoke(fileName, args);
|
||||
instance.PopupRequested += Instance_PopupRequested;
|
||||
instance.Terminated += Instance_Terminated;
|
||||
|
||||
button.RegisterInstance(instance);
|
||||
|
@ -120,9 +127,10 @@ namespace SafeExamBrowser.Browser
|
|||
UserAgent = settings.UseCustomUserAgent ? settings.CustomUserAgent : string.Empty
|
||||
};
|
||||
|
||||
logger.Debug($"CEF cache path is '{cefSettings.CachePath}'.");
|
||||
logger.Debug($"CEF log file is '{cefSettings.LogFile}'.");
|
||||
logger.Debug($"CEF log severity is '{cefSettings.LogSeverity}'.");
|
||||
logger.Debug($"Browser cache path: {cefSettings.CachePath}");
|
||||
logger.Debug($"Browser log file: {cefSettings.LogFile}");
|
||||
logger.Debug($"Browser log severity: {cefSettings.LogSeverity}");
|
||||
logger.Debug($"Browser engine version: Chromium {Cef.ChromiumVersion}, CEF {Cef.CefVersion}, CefSharp {Cef.CefSharpVersion}");
|
||||
|
||||
return cefSettings;
|
||||
}
|
||||
|
@ -139,6 +147,27 @@ namespace SafeExamBrowser.Browser
|
|||
}
|
||||
}
|
||||
|
||||
private void Instance_PopupRequested(PopupRequestedEventArgs args)
|
||||
{
|
||||
var popupSettings = new BrowserSettings
|
||||
{
|
||||
AllowAddressBar = false,
|
||||
AllowBackwardNavigation = false,
|
||||
AllowConfigurationDownloads = settings.AllowConfigurationDownloads,
|
||||
AllowDeveloperConsole = settings.AllowDeveloperConsole,
|
||||
AllowDownloads = settings.AllowDownloads,
|
||||
AllowForwardNavigation = false,
|
||||
AllowPageZoom = settings.AllowPageZoom,
|
||||
AllowPopups = settings.AllowPopups,
|
||||
AllowReloading = settings.AllowReloading,
|
||||
ShowReloadWarning = settings.ShowReloadWarning,
|
||||
StartUrl = args.Url
|
||||
};
|
||||
|
||||
logger.Info($"Received request to create new instance for '{args.Url}'...");
|
||||
CreateNewInstance(popupSettings);
|
||||
}
|
||||
|
||||
private void Instance_Terminated(InstanceIdentifier id)
|
||||
{
|
||||
instances.Remove(instances.FirstOrDefault(i => i.Id == id));
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using SafeExamBrowser.Browser.Events;
|
||||
using SafeExamBrowser.Browser.Handlers;
|
||||
using SafeExamBrowser.Contracts.Browser;
|
||||
using SafeExamBrowser.Contracts.Configuration;
|
||||
|
@ -16,6 +17,7 @@ using SafeExamBrowser.Contracts.I18n;
|
|||
using SafeExamBrowser.Contracts.Logging;
|
||||
using SafeExamBrowser.Contracts.UserInterface;
|
||||
using SafeExamBrowser.Contracts.UserInterface.Browser;
|
||||
using SafeExamBrowser.Contracts.UserInterface.MessageBox;
|
||||
using SafeExamBrowser.Contracts.UserInterface.Windows;
|
||||
|
||||
namespace SafeExamBrowser.Browser
|
||||
|
@ -26,6 +28,7 @@ namespace SafeExamBrowser.Browser
|
|||
private IBrowserControl control;
|
||||
private IBrowserWindow window;
|
||||
private bool isMainInstance;
|
||||
private IMessageBox messageBox;
|
||||
private IModuleLogger logger;
|
||||
private BrowserSettings settings;
|
||||
private IText text;
|
||||
|
@ -36,14 +39,16 @@ namespace SafeExamBrowser.Browser
|
|||
public IWindow Window { get { return window; } }
|
||||
|
||||
public event DownloadRequestedEventHandler ConfigurationDownloadRequested;
|
||||
public event NameChangedEventHandler NameChanged;
|
||||
public event InstanceTerminatedEventHandler Terminated;
|
||||
public event NameChangedEventHandler NameChanged;
|
||||
public event PopupRequestedEventHandler PopupRequested;
|
||||
|
||||
public BrowserApplicationInstance(
|
||||
AppConfig appConfig,
|
||||
BrowserSettings settings,
|
||||
InstanceIdentifier id,
|
||||
bool isMainInstance,
|
||||
IMessageBox messageBox,
|
||||
IModuleLogger logger,
|
||||
IText text,
|
||||
IUserInterfaceFactory uiFactory)
|
||||
|
@ -51,6 +56,7 @@ namespace SafeExamBrowser.Browser
|
|||
this.appConfig = appConfig;
|
||||
this.Id = id;
|
||||
this.isMainInstance = isMainInstance;
|
||||
this.messageBox = messageBox;
|
||||
this.logger = logger;
|
||||
this.settings = settings;
|
||||
this.text = text;
|
||||
|
@ -59,13 +65,18 @@ namespace SafeExamBrowser.Browser
|
|||
|
||||
internal void Initialize()
|
||||
{
|
||||
var controlLogger = logger.CloneFor($"{nameof(BrowserControl)} {Id}");
|
||||
var contextMenuHandler = new ContextMenuHandler(settings, text);
|
||||
var downloadLogger = logger.CloneFor($"{nameof(DownloadHandler)} {Id}");
|
||||
var downloadHandler = new DownloadHandler(appConfig, settings, downloadLogger);
|
||||
var keyboardHandler = new KeyboardHandler();
|
||||
var lifeSpanHandler = new LifeSpanHandler();
|
||||
var requestHandler = new RequestHandler(appConfig);
|
||||
|
||||
downloadHandler.ConfigurationDownloadRequested += DownloadHandler_ConfigurationDownloadRequested;
|
||||
keyboardHandler.ReloadRequested += KeyboardHandler_ReloadRequested;
|
||||
lifeSpanHandler.PopupRequested += LifeSpanHandler_PopupRequested;
|
||||
|
||||
control = new BrowserControl(appConfig, settings, downloadHandler, controlLogger, text);
|
||||
control = new BrowserControl(contextMenuHandler, downloadHandler, keyboardHandler, lifeSpanHandler, requestHandler, settings.StartUrl);
|
||||
control.AddressChanged += Control_AddressChanged;
|
||||
control.LoadingStateChanged += Control_LoadingStateChanged;
|
||||
control.TitleChanged += Control_TitleChanged;
|
||||
|
@ -84,8 +95,36 @@ namespace SafeExamBrowser.Browser
|
|||
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}'.");
|
||||
window.UpdateAddress(address);
|
||||
}
|
||||
|
||||
|
@ -106,7 +145,6 @@ namespace SafeExamBrowser.Browser
|
|||
{
|
||||
args.BrowserWindow = window;
|
||||
logger.Debug($"Forwarding download request for configuration file '{fileName}'.");
|
||||
|
||||
ConfigurationDownloadRequested?.Invoke(fileName, args);
|
||||
}
|
||||
else
|
||||
|
@ -115,6 +153,24 @@ namespace SafeExamBrowser.Browser
|
|||
}
|
||||
}
|
||||
|
||||
private void KeyboardHandler_ReloadRequested()
|
||||
{
|
||||
HandleReloadRequest();
|
||||
}
|
||||
|
||||
private void LifeSpanHandler_PopupRequested(PopupRequestedEventArgs args)
|
||||
{
|
||||
if (settings.AllowPopups)
|
||||
{
|
||||
logger.Debug($"Forwarding request to open new window for '{args.Url}'...");
|
||||
PopupRequested?.Invoke(args);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Debug($"Blocked attempt to open new window for '{args.Url}'.");
|
||||
}
|
||||
}
|
||||
|
||||
private void Window_AddressChanged(string address)
|
||||
{
|
||||
logger.Debug($"The user requested to navigate to '{address}'.");
|
||||
|
@ -123,19 +179,18 @@ namespace SafeExamBrowser.Browser
|
|||
|
||||
private void Window_ReloadRequested()
|
||||
{
|
||||
logger.Debug($"The user requested to reload the current page.");
|
||||
control.Reload();
|
||||
HandleReloadRequest();
|
||||
}
|
||||
|
||||
private void Window_BackwardNavigationRequested()
|
||||
{
|
||||
logger.Debug($"The user requested to navigate backwards.");
|
||||
logger.Debug($"Navigating forwards...");
|
||||
control.NavigateBackwards();
|
||||
}
|
||||
|
||||
private void Window_ForwardNavigationRequested()
|
||||
{
|
||||
logger.Debug($"The user requested to navigate forwards.");
|
||||
logger.Debug($"Navigating backwards...");
|
||||
control.NavigateForwards();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,27 +6,20 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using CefSharp;
|
||||
using CefSharp.WinForms;
|
||||
using SafeExamBrowser.Browser.Handlers;
|
||||
using SafeExamBrowser.Contracts.Configuration;
|
||||
using SafeExamBrowser.Contracts.I18n;
|
||||
using SafeExamBrowser.Contracts.Logging;
|
||||
using SafeExamBrowser.Contracts.UserInterface.Browser;
|
||||
using SafeExamBrowser.Contracts.UserInterface.Browser.Events;
|
||||
using BrowserSettings = SafeExamBrowser.Contracts.Configuration.Settings.BrowserSettings;
|
||||
|
||||
namespace SafeExamBrowser.Browser
|
||||
{
|
||||
internal class BrowserControl : ChromiumWebBrowser, IBrowserControl
|
||||
{
|
||||
private AppConfig appConfig;
|
||||
private BrowserSettings settings;
|
||||
private IContextMenuHandler contextMenuHandler;
|
||||
private IDownloadHandler downloadHandler;
|
||||
private ILogger logger;
|
||||
private IText text;
|
||||
private IKeyboardHandler keyboardHandler;
|
||||
private ILifeSpanHandler lifeSpanHandler;
|
||||
private IRequestHandler requestHandler;
|
||||
|
||||
private AddressChangedEventHandler addressChanged;
|
||||
private LoadingStateChangedEventHandler loadingStateChanged;
|
||||
|
@ -51,30 +44,31 @@ namespace SafeExamBrowser.Browser
|
|||
}
|
||||
|
||||
public BrowserControl(
|
||||
AppConfig appConfig,
|
||||
BrowserSettings settings,
|
||||
IContextMenuHandler contextMenuHandler,
|
||||
IDownloadHandler downloadHandler,
|
||||
ILogger logger,
|
||||
IText text) : base(settings.StartUrl)
|
||||
IKeyboardHandler keyboardHandler,
|
||||
ILifeSpanHandler lifeSpanHandler,
|
||||
IRequestHandler requestHandler,
|
||||
string url) : base(url)
|
||||
{
|
||||
this.appConfig = appConfig;
|
||||
this.contextMenuHandler = contextMenuHandler;
|
||||
this.downloadHandler = downloadHandler;
|
||||
this.logger = logger;
|
||||
this.settings = settings;
|
||||
this.text = text;
|
||||
this.keyboardHandler = keyboardHandler;
|
||||
this.lifeSpanHandler = lifeSpanHandler;
|
||||
this.requestHandler = requestHandler;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
AddressChanged += BrowserControl_AddressChanged;
|
||||
AddressChanged += (o, args) => addressChanged?.Invoke(args.Address);
|
||||
LoadingStateChanged += (o, args) => loadingStateChanged?.Invoke(args.IsLoading);
|
||||
MouseWheel += BrowserControl_MouseWheel;
|
||||
TitleChanged += (o, args) => titleChanged?.Invoke(args.Title);
|
||||
|
||||
DownloadHandler = downloadHandler;
|
||||
KeyboardHandler = new KeyboardHandler(settings);
|
||||
MenuHandler = new ContextMenuHandler(settings, text);
|
||||
RequestHandler = new RequestHandler(appConfig);
|
||||
KeyboardHandler = keyboardHandler;
|
||||
LifeSpanHandler = lifeSpanHandler;
|
||||
MenuHandler = contextMenuHandler;
|
||||
RequestHandler = requestHandler;
|
||||
}
|
||||
|
||||
public void NavigateBackwards()
|
||||
|
@ -89,37 +83,12 @@ namespace SafeExamBrowser.Browser
|
|||
|
||||
public void NavigateTo(string address)
|
||||
{
|
||||
if (!String.IsNullOrWhiteSpace(address))
|
||||
{
|
||||
Load(address);
|
||||
}
|
||||
Load(address);
|
||||
}
|
||||
|
||||
public void Reload()
|
||||
{
|
||||
GetBrowser().Reload();
|
||||
}
|
||||
|
||||
private void BrowserControl_AddressChanged(object sender, AddressChangedEventArgs args)
|
||||
{
|
||||
logger.Debug($"Navigated to '{args.Address}'.");
|
||||
addressChanged?.Invoke(args.Address);
|
||||
}
|
||||
|
||||
private void BrowserControl_MouseWheel(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (settings.AllowPageZoom && ModifierKeys == Keys.Control)
|
||||
{
|
||||
var browser = GetBrowser();
|
||||
|
||||
browser.GetZoomLevelAsync().ContinueWith(task =>
|
||||
{
|
||||
if (task.IsCompleted)
|
||||
{
|
||||
browser.SetZoomLevel(task.Result + e.Delta * 0.1);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
15
SafeExamBrowser.Browser/Events/PopupRequestedEventArgs.cs
Normal file
15
SafeExamBrowser.Browser/Events/PopupRequestedEventArgs.cs
Normal 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.Browser.Events
|
||||
{
|
||||
internal class PopupRequestedEventArgs
|
||||
{
|
||||
public string Url { get; set; }
|
||||
}
|
||||
}
|
12
SafeExamBrowser.Browser/Events/PopupRequestedEventHandler.cs
Normal file
12
SafeExamBrowser.Browser/Events/PopupRequestedEventHandler.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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 PopupRequestedEventHandler(PopupRequestedEventArgs args);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
|
@ -13,7 +13,7 @@ using BrowserSettings = SafeExamBrowser.Contracts.Configuration.Settings.Browser
|
|||
namespace SafeExamBrowser.Browser.Handlers
|
||||
{
|
||||
/// <remarks>
|
||||
/// See https://cefsharp.github.io/api/63.0.0/html/T_CefSharp_IContextMenuHandler.htm.
|
||||
/// See https://cefsharp.github.io/api/67.0.0/html/T_CefSharp_IContextMenuHandler.htm.
|
||||
/// </remarks>
|
||||
internal class ContextMenuHandler : IContextMenuHandler
|
||||
{
|
||||
|
|
|
@ -19,7 +19,7 @@ using BrowserSettings = SafeExamBrowser.Contracts.Configuration.Settings.Browser
|
|||
namespace SafeExamBrowser.Browser.Handlers
|
||||
{
|
||||
/// <remarks>
|
||||
/// See https://cefsharp.github.io/api/63.0.0/html/T_CefSharp_IDownloadHandler.htm.
|
||||
/// See https://cefsharp.github.io/api/67.0.0/html/T_CefSharp_IDownloadHandler.htm.
|
||||
/// </remarks>
|
||||
internal class DownloadHandler : IDownloadHandler
|
||||
{
|
||||
|
|
|
@ -8,21 +8,16 @@
|
|||
|
||||
using System.Windows.Forms;
|
||||
using CefSharp;
|
||||
using BrowserSettings = SafeExamBrowser.Contracts.Configuration.Settings.BrowserSettings;
|
||||
using SafeExamBrowser.Browser.Events;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Handlers
|
||||
{
|
||||
/// <remarks>
|
||||
/// See https://cefsharp.github.io/api/63.0.0/html/T_CefSharp_IKeyboardHandler.htm.
|
||||
/// See https://cefsharp.github.io/api/67.0.0/html/T_CefSharp_IKeyboardHandler.htm.
|
||||
/// </remarks>
|
||||
internal class KeyboardHandler : IKeyboardHandler
|
||||
{
|
||||
private BrowserSettings settings;
|
||||
|
||||
public KeyboardHandler(BrowserSettings settings)
|
||||
{
|
||||
this.settings = settings;
|
||||
}
|
||||
public event ReloadRequestedEventHandler ReloadRequested;
|
||||
|
||||
public bool OnKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey)
|
||||
{
|
||||
|
@ -31,9 +26,9 @@ namespace SafeExamBrowser.Browser.Handlers
|
|||
|
||||
public bool OnPreKeyEvent(IWebBrowser browserControl, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut)
|
||||
{
|
||||
if (settings.AllowReloading && type == KeyType.KeyUp && windowsKeyCode == (int) Keys.F5)
|
||||
if (type == KeyType.KeyUp && windowsKeyCode == (int) Keys.F5)
|
||||
{
|
||||
browserControl.Reload();
|
||||
ReloadRequested?.Invoke();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
44
SafeExamBrowser.Browser/Handlers/LifeSpanHandler.cs
Normal file
44
SafeExamBrowser.Browser/Handlers/LifeSpanHandler.cs
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
using CefSharp;
|
||||
using SafeExamBrowser.Browser.Events;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Handlers
|
||||
{
|
||||
/// <remarks>
|
||||
/// See https://cefsharp.github.io/api/67.0.0/html/T_CefSharp_ILifeSpanHandler.htm.
|
||||
/// </remarks>
|
||||
internal class LifeSpanHandler : ILifeSpanHandler
|
||||
{
|
||||
public event PopupRequestedEventHandler PopupRequested;
|
||||
|
||||
public bool DoClose(IWebBrowser chromiumWebBrowser, IBrowser browser)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnAfterCreated(IWebBrowser chromiumWebBrowser, IBrowser browser)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnBeforeClose(IWebBrowser chromiumWebBrowser, IBrowser browser)
|
||||
{
|
||||
}
|
||||
|
||||
public bool OnBeforePopup(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser)
|
||||
{
|
||||
var args = new PopupRequestedEventArgs { Url = targetUrl };
|
||||
|
||||
newBrowser = default(IWebBrowser);
|
||||
PopupRequested?.Invoke(args);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@ using SafeExamBrowser.Contracts.Configuration;
|
|||
namespace SafeExamBrowser.Browser.Handlers
|
||||
{
|
||||
/// <remarks>
|
||||
/// See https://cefsharp.github.io/api/63.0.0/html/T_CefSharp_Handler_DefaultRequestHandler.htm.
|
||||
/// See https://cefsharp.github.io/api/67.0.0/html/T_CefSharp_Handler_DefaultRequestHandler.htm.
|
||||
/// </remarks>
|
||||
internal class RequestHandler : DefaultRequestHandler
|
||||
{
|
||||
|
|
|
@ -66,6 +66,9 @@
|
|||
<Compile Include="BrowserApplicationInfo.cs" />
|
||||
<Compile Include="BrowserApplicationInstance.cs" />
|
||||
<Compile Include="BrowserInstanceIdentifier.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>
|
||||
|
@ -73,6 +76,7 @@
|
|||
<Compile Include="BrowserIconResource.cs" />
|
||||
<Compile Include="Handlers\DownloadHandler.cs" />
|
||||
<Compile Include="Handlers\KeyboardHandler.cs" />
|
||||
<Compile Include="Handlers\LifeSpanHandler.cs" />
|
||||
<Compile Include="Handlers\RequestHandler.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -171,7 +171,7 @@ namespace SafeExamBrowser.Client
|
|||
private IOperation BuildBrowserOperation()
|
||||
{
|
||||
var moduleLogger = new ModuleLogger(logger, "BrowserController");
|
||||
var browserController = new BrowserApplicationController(configuration.AppConfig, configuration.Settings.Browser, moduleLogger, text, uiFactory);
|
||||
var browserController = new BrowserApplicationController(configuration.AppConfig, configuration.Settings.Browser, messageBox, moduleLogger, text, uiFactory);
|
||||
var browserInfo = new BrowserApplicationInfo();
|
||||
var operation = new BrowserOperation(browserController, browserInfo, logger, Taskbar, uiFactory);
|
||||
|
||||
|
|
|
@ -13,6 +13,39 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
{
|
||||
internal partial class DataMapper
|
||||
{
|
||||
private void MapAllowNavigation(Settings settings, object value)
|
||||
{
|
||||
if (value is bool allow)
|
||||
{
|
||||
settings.Browser.AllowBackwardNavigation = allow;
|
||||
settings.Browser.AllowForwardNavigation = allow;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapAllowPageZoom(Settings settings, object value)
|
||||
{
|
||||
if (value is bool allow)
|
||||
{
|
||||
settings.Browser.AllowPageZoom = allow;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapAllowPopups(Settings settings, object value)
|
||||
{
|
||||
if (value is bool block)
|
||||
{
|
||||
settings.Browser.AllowPopups = !block;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapAllowReload(Settings settings, object value)
|
||||
{
|
||||
if (value is bool allow)
|
||||
{
|
||||
settings.Browser.AllowReloading = allow;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapMainWindowMode(Settings settings, object value)
|
||||
{
|
||||
const int FULLSCREEN = 1;
|
||||
|
@ -23,11 +56,11 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
}
|
||||
}
|
||||
|
||||
private void MapPageZoom(Settings settings, object value)
|
||||
private void MapShowReloadWarning(Settings settings, object value)
|
||||
{
|
||||
if (value is bool enabled)
|
||||
if (value is bool show)
|
||||
{
|
||||
settings.Browser.AllowPageZoom = enabled;
|
||||
settings.Browser.ShowReloadWarning = show;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,12 +27,24 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
{
|
||||
switch (key)
|
||||
{
|
||||
case Keys.Browser.EnablePageZoom:
|
||||
MapPageZoom(settings, value);
|
||||
case Keys.Browser.AllowNavigation:
|
||||
MapAllowNavigation(settings, value);
|
||||
break;
|
||||
case Keys.Browser.AllowPageZoom:
|
||||
MapAllowPageZoom(settings, value);
|
||||
break;
|
||||
case Keys.Browser.AllowPopups:
|
||||
MapAllowPopups(settings, value);
|
||||
break;
|
||||
case Keys.Browser.AllowReload:
|
||||
MapAllowReload(settings, value);
|
||||
break;
|
||||
case Keys.Browser.MainWindowMode:
|
||||
MapMainWindowMode(settings, value);
|
||||
break;
|
||||
case Keys.Browser.ShowReloadWarning:
|
||||
MapShowReloadWarning(settings, value);
|
||||
break;
|
||||
case Keys.ConfigurationFile.ConfigurationPurpose:
|
||||
MapConfigurationMode(settings, value);
|
||||
break;
|
||||
|
|
|
@ -20,10 +20,14 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
|
||||
internal static class Browser
|
||||
{
|
||||
internal const string AllowNavigation = "allowBrowsingBackForward";
|
||||
internal const string AllowPageZoom = "enableZoomPage";
|
||||
internal const string AllowPopups = "blockPopUpWindows";
|
||||
internal const string AllowReload = "browserWindowAllowReload";
|
||||
internal const string CustomUserAgentDesktop = "browserUserAgentWinDesktopModeCustom";
|
||||
internal const string CustomUserAgentMobile = "browserUserAgentWinTouchModeCustom";
|
||||
internal const string EnablePageZoom = "enableZoomPage";
|
||||
internal const string MainWindowMode = "browserViewMode";
|
||||
internal const string ShowReloadWarning = "showReloadWarning";
|
||||
internal const string UserAgentModeDesktop = "browserUserAgentWinDesktopMode";
|
||||
internal const string UserAgentModeMobile = "browserUserAgentWinTouchMode";
|
||||
}
|
||||
|
|
|
@ -17,42 +17,47 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
|
|||
public class BrowserSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the user should be allowed to change the URL of a browser window.
|
||||
/// Determines whether the user will be allowed to change the URL of a browser window.
|
||||
/// </summary>
|
||||
public bool AllowAddressBar { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the user should be allowed to navigate backwards in a browser window.
|
||||
/// Determines whether the user will be allowed to navigate backwards in a browser window.
|
||||
/// </summary>
|
||||
public bool AllowBackwardNavigation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the user should be allowed to download configuration files.
|
||||
/// Determines whether the user will be allowed to download configuration files.
|
||||
/// </summary>
|
||||
public bool AllowConfigurationDownloads { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the user should be allowed to open the developer console of a browser window.
|
||||
/// Determines whether the user will be allowed to open the developer console of a browser window.
|
||||
/// </summary>
|
||||
public bool AllowDeveloperConsole { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the user should be allowed to download files (excluding configuration files).
|
||||
/// Determines whether the user will be allowed to download files (excluding configuration files).
|
||||
/// </summary>
|
||||
public bool AllowDownloads { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the user should be allowed to navigate forwards in a browser window.
|
||||
/// Determines whether the user will be allowed to navigate forwards in a browser window.
|
||||
/// </summary>
|
||||
public bool AllowForwardNavigation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the user should be allowed to zoom webpages.
|
||||
/// Determines whether the user will be allowed to zoom webpages.
|
||||
/// </summary>
|
||||
public bool AllowPageZoom { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the user should be allowed to reload webpages.
|
||||
/// Determines whether popup windows will be opened or not.
|
||||
/// </summary>
|
||||
public bool AllowPopups { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the user will be allowed to reload webpages.
|
||||
/// </summary>
|
||||
public bool AllowReloading { get; set; }
|
||||
|
||||
|
@ -62,17 +67,22 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
|
|||
public string CustomUserAgent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the main browser window should be rendered in fullscreen mode, i.e. without window frame.
|
||||
/// Determines whether the main browser window will be rendered in fullscreen mode, i.e. without window frame.
|
||||
/// </summary>
|
||||
public bool FullScreenMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The start URL with which a new browser window should be loaded.
|
||||
/// Determines whether the user will need to confirm every reload attempt.
|
||||
/// </summary>
|
||||
public bool ShowReloadWarning { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The start URL with which a new browser window will be loaded.
|
||||
/// </summary>
|
||||
public string StartUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a custom user agent should be used for all requests, see <see cref="CustomUserAgent"/>.
|
||||
/// Determines whether a custom user agent will be used for all requests, see <see cref="CustomUserAgent"/>.
|
||||
/// </summary>
|
||||
public bool UseCustomUserAgent { get; set; }
|
||||
}
|
||||
|
|
|
@ -42,6 +42,8 @@ namespace SafeExamBrowser.Contracts.I18n
|
|||
MessageBox_ReconfigurationErrorTitle,
|
||||
MessageBox_ReconfigurationQuestion,
|
||||
MessageBox_ReconfigurationQuestionTitle,
|
||||
MessageBox_ReloadConfirmation,
|
||||
MessageBox_ReloadConfirmationTitle,
|
||||
MessageBox_ShutdownError,
|
||||
MessageBox_ShutdownErrorTitle,
|
||||
MessageBox_SingleInstance,
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace SafeExamBrowser.Contracts.Monitoring
|
|||
/// </summary>
|
||||
public enum KeyState
|
||||
{
|
||||
None = 0,
|
||||
Unknown = 0,
|
||||
Pressed,
|
||||
Released
|
||||
}
|
||||
|
|
|
@ -13,7 +13,8 @@ namespace SafeExamBrowser.Contracts.Monitoring
|
|||
/// </summary>
|
||||
public enum MouseButton
|
||||
{
|
||||
None = 0,
|
||||
Unknown = 0,
|
||||
Auxiliary,
|
||||
Left,
|
||||
Middle,
|
||||
Right
|
||||
|
|
|
@ -84,6 +84,12 @@
|
|||
<Entry key="MessageBox_ReconfigurationQuestionTitle">
|
||||
Configuration Detected
|
||||
</Entry>
|
||||
<Entry key="MessageBox_ReloadConfirmation">
|
||||
Would you like to reload the current page?
|
||||
</Entry>
|
||||
<Entry key="MessageBox_ReloadConfirmationTitle">
|
||||
Reload?
|
||||
</Entry>
|
||||
<Entry key="MessageBox_ShutdownError">
|
||||
An unexpected error occurred during the shutdown procedure! Please consult the application log for more information...
|
||||
</Entry>
|
||||
|
|
|
@ -27,6 +27,7 @@ namespace SafeExamBrowser.Monitoring.Mouse
|
|||
{
|
||||
var block = false;
|
||||
|
||||
block |= button == MouseButton.Auxiliary;
|
||||
block |= button == MouseButton.Middle && !settings.AllowMiddleButton;
|
||||
block |= button == MouseButton.Right && !settings.AllowRightButton;
|
||||
|
||||
|
|
|
@ -5,8 +5,7 @@
|
|||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:fa="http://schemas.fontawesome.io/icons/"
|
||||
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop"
|
||||
mc:Ignorable="d" Title="BrowserWindow" Background="#FFF0F0F0" Height="500" Width="750" MinHeight="250" MinWidth="250"
|
||||
WindowState="Maximized" Icon=".\Images\SafeExamBrowser.ico">
|
||||
mc:Ignorable="d" Title="BrowserWindow" Background="#FFF0F0F0" Height="500" Width="750" MinHeight="250" MinWidth="250" Icon=".\Images\SafeExamBrowser.ico">
|
||||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
|
|
|
@ -153,12 +153,26 @@ namespace SafeExamBrowser.UserInterface.Desktop
|
|||
|
||||
private void ApplySettings()
|
||||
{
|
||||
if (IsMainWindow && settings.FullScreenMode)
|
||||
if (IsMainWindow)
|
||||
{
|
||||
MaxHeight = SystemParameters.WorkArea.Height;
|
||||
ResizeMode = ResizeMode.NoResize;
|
||||
WindowState = WindowState.Maximized;
|
||||
WindowStyle = WindowStyle.None;
|
||||
if (settings.FullScreenMode)
|
||||
{
|
||||
MaxHeight = SystemParameters.WorkArea.Height;
|
||||
ResizeMode = ResizeMode.NoResize;
|
||||
WindowState = WindowState.Maximized;
|
||||
WindowStyle = WindowStyle.None;
|
||||
}
|
||||
else
|
||||
{
|
||||
WindowState = WindowState.Maximized;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Top = 0;
|
||||
Left = SystemParameters.WorkArea.Width / 2;
|
||||
Height = SystemParameters.WorkArea.Height;
|
||||
Width = SystemParameters.WorkArea.Width / 2;
|
||||
}
|
||||
|
||||
UrlTextBox.IsEnabled = settings.AllowAddressBar;
|
||||
|
|
|
@ -11,17 +11,17 @@
|
|||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="../Templates/Buttons.xaml" />
|
||||
<ResourceDictionary Source="../Templates/Colors.xaml" />
|
||||
<ResourceDictionary Source="../Templates/ScrollViewers.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Popup x:Name="InstancePopup" IsOpen="False" Placement="Top" PlacementTarget="{Binding ElementName=Button}">
|
||||
<ScrollViewer x:Name="InstanceScrollViewer" Background="{StaticResource BackgroundBrush}" VerticalScrollBarVisibility="Auto">
|
||||
<ScrollViewer.Resources>
|
||||
<s:Double x:Key="{x:Static SystemParameters.VerticalScrollBarWidthKey}">5</s:Double>
|
||||
</ScrollViewer.Resources>
|
||||
<StackPanel x:Name="InstanceStackPanel" />
|
||||
</ScrollViewer>
|
||||
<Border Background="LightGray" BorderBrush="Gray" BorderThickness="0.75,0.75,0.75,0">
|
||||
<ScrollViewer x:Name="InstanceScrollViewer" MaxHeight="400" VerticalScrollBarVisibility="Auto" Template="{StaticResource SmallBarScrollViewer}">
|
||||
<StackPanel x:Name="InstanceStackPanel" />
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</Popup>
|
||||
<Button x:Name="Button" Background="{StaticResource BackgroundBrush}" Click="Button_Click" Padding="4" Template="{StaticResource TaskbarButton}" Width="50" />
|
||||
</Grid>
|
||||
|
|
|
@ -6,13 +6,16 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using SafeExamBrowser.Contracts.Core;
|
||||
using System.Windows.Threading;
|
||||
using SafeExamBrowser.Contracts.Configuration;
|
||||
using SafeExamBrowser.Contracts.Core;
|
||||
using SafeExamBrowser.Contracts.UserInterface.Taskbar;
|
||||
using SafeExamBrowser.Contracts.UserInterface.Taskbar.Events;
|
||||
using SafeExamBrowser.UserInterface.Desktop.Utilities;
|
||||
|
@ -36,13 +39,16 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
|
|||
|
||||
public void RegisterInstance(IApplicationInstance instance)
|
||||
{
|
||||
var instanceButton = new ApplicationInstanceButton(instance, info);
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
var instanceButton = new ApplicationInstanceButton(instance, info);
|
||||
|
||||
instanceButton.Clicked += (id) => Clicked?.Invoke(id);
|
||||
instance.Terminated += (id) => Instance_OnTerminated(id, instanceButton);
|
||||
instanceButton.Clicked += (id) => Clicked?.Invoke(id);
|
||||
instance.Terminated += (id) => Instance_OnTerminated(id, instanceButton);
|
||||
|
||||
instances.Add(instance);
|
||||
InstanceStackPanel.Children.Add(instanceButton);
|
||||
instances.Add(instance);
|
||||
InstanceStackPanel.Children.Add(instanceButton);
|
||||
});
|
||||
}
|
||||
|
||||
private void InitializeApplicationButton()
|
||||
|
@ -53,13 +59,13 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
|
|||
Button.Content = IconResourceLoader.Load(info.IconResource);
|
||||
|
||||
Button.MouseEnter += (o, args) => InstancePopup.IsOpen = instances.Count > 1;
|
||||
Button.MouseLeave += (o, args) => InstancePopup.IsOpen = InstancePopup.IsMouseOver;
|
||||
InstancePopup.MouseLeave += (o, args) => InstancePopup.IsOpen = IsMouseOver;
|
||||
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => InstancePopup.IsOpen = InstancePopup.IsMouseOver));
|
||||
InstancePopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => InstancePopup.IsOpen = IsMouseOver));
|
||||
|
||||
InstancePopup.Opened += (o, args) =>
|
||||
{
|
||||
Background = Brushes.LightBlue;
|
||||
Button.Background = Brushes.LightBlue;
|
||||
Background = Brushes.LightGray;
|
||||
Button.Background = Brushes.LightGray;
|
||||
};
|
||||
|
||||
InstancePopup.Closed += (o, args) =>
|
||||
|
@ -67,14 +73,6 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
|
|||
Background = originalBrush;
|
||||
Button.Background = originalBrush;
|
||||
};
|
||||
|
||||
InstanceStackPanel.SizeChanged += (o, args) =>
|
||||
{
|
||||
if (instances.Count > 9)
|
||||
{
|
||||
InstanceScrollViewer.MaxHeight = InstanceScrollViewer.ActualHeight;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void Button_Click(object sender, RoutedEventArgs e)
|
||||
|
@ -91,8 +89,11 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
|
|||
|
||||
private void Instance_OnTerminated(InstanceIdentifier id, ApplicationInstanceButton instanceButton)
|
||||
{
|
||||
instances.Remove(instances.FirstOrDefault(i => i.Id == id));
|
||||
InstanceStackPanel.Children.Remove(instanceButton);
|
||||
Dispatcher.BeginInvoke(new Action(() =>
|
||||
{
|
||||
instances.Remove(instances.FirstOrDefault(i => i.Id == id));
|
||||
InstanceStackPanel.Children.Remove(instanceButton);
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,10 +14,10 @@
|
|||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Button x:Name="Button" Background="{StaticResource BackgroundBrush}" Click="Button_Click" Height="25" Template="{StaticResource TaskbarButton}">
|
||||
<Button x:Name="Button" Background="Transparent" Click="Button_Click" Height="40" Padding="10" Template="{StaticResource TaskbarButton}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<ContentControl x:Name="Icon" HorizontalAlignment="Left" />
|
||||
<TextBlock x:Name="Text" Padding="5,0,5,0" HorizontalAlignment="Left" VerticalAlignment="Center" />
|
||||
<ContentControl x:Name="Icon" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="0,0,10,0" />
|
||||
<TextBlock x:Name="Text" HorizontalAlignment="Left" VerticalAlignment="Center" Padding="5" />
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Button x:Name="Button" Height="40" Padding="10,0" Template="{StaticResource TaskbarButton}">
|
||||
<Button x:Name="Button" Background="Transparent" Height="40" Padding="10,0" Template="{StaticResource TaskbarButton}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Popup x:Name="Popup" IsOpen="False" Placement="Top" PlacementTarget="{Binding ElementName=Button}">
|
||||
<Border Background="LightGray" BorderBrush="Gray" BorderThickness="0.5,0.5,0.5,0">
|
||||
<ScrollViewer x:Name="LayoutsScrollViewer" Background="{StaticResource BackgroundBrush}" MaxHeight="250" VerticalScrollBarVisibility="Auto">
|
||||
<Border Background="LightGray" BorderBrush="Gray" BorderThickness="0.75,0.75,0.75,0">
|
||||
<ScrollViewer x:Name="LayoutsScrollViewer" MaxHeight="250" VerticalScrollBarVisibility="Auto">
|
||||
<ScrollViewer.Resources>
|
||||
<s:Double x:Key="{x:Static SystemParameters.VerticalScrollBarWidthKey}">5</s:Double>
|
||||
</ScrollViewer.Resources>
|
||||
|
@ -28,7 +28,11 @@
|
|||
</Popup>
|
||||
<Button x:Name="Button" Background="Transparent" Template="{StaticResource TaskbarButton}" Padding="5" Width="40">
|
||||
<Grid>
|
||||
<fa:ImageAwesome Panel.ZIndex="1" Foreground="LightGray" Icon="KeyboardOutline" VerticalAlignment="Center" />
|
||||
<fa:ImageAwesome Panel.ZIndex="1" Foreground="LightGray" Icon="KeyboardOutline" VerticalAlignment="Center">
|
||||
<fa:ImageAwesome.Effect>
|
||||
<DropShadowEffect Color="White" BlurRadius="5" Direction="0" Opacity="1" ShadowDepth="0" />
|
||||
</fa:ImageAwesome.Effect>
|
||||
</fa:ImageAwesome>
|
||||
<Viewbox Panel.ZIndex="2" Stretch="Uniform">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock x:Name="LayoutCultureCode" FontWeight="Bold" TextAlignment="Center" Text="ENG">
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using SafeExamBrowser.Contracts.SystemComponents;
|
||||
|
@ -62,13 +63,13 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
|
|||
var originalBrush = Button.Background;
|
||||
|
||||
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
|
||||
Button.MouseLeave += (o, args) => Popup.IsOpen = Popup.IsMouseOver;
|
||||
Popup.MouseLeave += (o, args) => Popup.IsOpen = IsMouseOver;
|
||||
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver));
|
||||
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver));
|
||||
|
||||
Popup.Opened += (o, args) =>
|
||||
{
|
||||
Background = Brushes.LightBlue;
|
||||
Button.Background = Brushes.LightBlue;
|
||||
Background = Brushes.LightGray;
|
||||
Button.Background = Brushes.LightGray;
|
||||
};
|
||||
|
||||
Popup.Closed += (o, args) =>
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Popup x:Name="Popup" IsOpen="False" Placement="Top" PlacementTarget="{Binding ElementName=Button}">
|
||||
<Border Background="LightGray" BorderBrush="Gray" BorderThickness="0.5,0.5,0.5,0" MaxWidth="250" Padding="20,10,20,20">
|
||||
<Grid>
|
||||
<Border Background="LightGray" BorderBrush="Gray" BorderThickness="0.75,0.75,0.75,0">
|
||||
<Grid MaxWidth="250" Margin="20,10,20,20">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
</ResourceDictionary>
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Button x:Name="Button" Height="40" Padding="10,0" Template="{StaticResource TaskbarButton}">
|
||||
<Button x:Name="Button" Background="Transparent" Height="40" Padding="10,0" Template="{StaticResource TaskbarButton}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Popup x:Name="Popup" IsOpen="False" Placement="Top" PlacementTarget="{Binding ElementName=Button}">
|
||||
<Border Background="LightGray" BorderBrush="Gray" BorderThickness="0.5,0.5,0.5,0">
|
||||
<ScrollViewer Background="{StaticResource BackgroundBrush}" MaxHeight="250" VerticalScrollBarVisibility="Auto">
|
||||
<Border Background="LightGray" BorderBrush="Gray" BorderThickness="0.75,0.75,0.75,0">
|
||||
<ScrollViewer MaxHeight="250" VerticalScrollBarVisibility="Auto">
|
||||
<ScrollViewer.Resources>
|
||||
<s:Double x:Key="{x:Static SystemParameters.VerticalScrollBarWidthKey}">5</s:Double>
|
||||
</ScrollViewer.Resources>
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
|
@ -114,13 +115,13 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
|
|||
|
||||
SignalStrengthIcon.Child = GetIcon(0);
|
||||
Button.Click += (o, args) => Popup.IsOpen = !Popup.IsOpen;
|
||||
Button.MouseLeave += (o, args) => Popup.IsOpen = Popup.IsMouseOver;
|
||||
Popup.MouseLeave += (o, args) => Popup.IsOpen = IsMouseOver;
|
||||
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = Popup.IsMouseOver));
|
||||
Popup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => Popup.IsOpen = IsMouseOver));
|
||||
|
||||
Popup.Opened += (o, args) =>
|
||||
{
|
||||
Background = Brushes.LightBlue;
|
||||
Button.Background = Brushes.LightBlue;
|
||||
Background = Brushes.LightGray;
|
||||
Button.Background = Brushes.LightGray;
|
||||
};
|
||||
|
||||
Popup.Closed += (o, args) =>
|
||||
|
|
|
@ -162,6 +162,10 @@
|
|||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Templates\ScrollViewers.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Resource Include="Images\Battery.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
|
|
|
@ -8,6 +8,13 @@
|
|||
mc:Ignorable="d"
|
||||
Title="Taskbar" Background="#FFF0F0F0" Height="40" Width="750" WindowStyle="None" Topmost="True" Visibility="Visible"
|
||||
ResizeMode="NoResize" Icon="./Images/SafeExamBrowser.ico">
|
||||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="./Templates/ScrollViewers.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
|
@ -16,10 +23,8 @@
|
|||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="40" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ScrollViewer Grid.Column="0" x:Name="ApplicationScrollViewer" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Auto">
|
||||
<ScrollViewer.Resources>
|
||||
<s:Double x:Key="{x:Static SystemParameters.HorizontalScrollBarHeightKey}">5</s:Double>
|
||||
</ScrollViewer.Resources>
|
||||
<ScrollViewer Grid.Column="0" x:Name="ApplicationScrollViewer" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Auto"
|
||||
Template="{StaticResource SmallBarScrollViewer}">
|
||||
<StackPanel x:Name="ApplicationStackPanel" Orientation="Horizontal" />
|
||||
</ScrollViewer>
|
||||
<StackPanel Grid.Column="1" x:Name="NotificationStackPanel" Orientation="Horizontal" VerticalAlignment="Stretch" />
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Templates">
|
||||
<ControlTemplate x:Key="Thumb" TargetType="{x:Type Thumb}">
|
||||
<Border Background="DarkGray" Height="Auto" Width="Auto" />
|
||||
</ControlTemplate>
|
||||
<ControlTemplate x:Key="VerticalScrollBar" TargetType="{x:Type ScrollBar}">
|
||||
<Track Name="PART_Track" Height="Auto" IsDirectionReversed="True" Width="10">
|
||||
<Track.Thumb>
|
||||
<Thumb Template="{StaticResource Thumb}" Focusable="False" Margin="2.5" OverridesDefaultStyle="True" SnapsToDevicePixels="True" />
|
||||
</Track.Thumb>
|
||||
</Track>
|
||||
</ControlTemplate>
|
||||
<ControlTemplate x:Key="HorizontalScrollBar" TargetType="{x:Type ScrollBar}">
|
||||
<Track Name="PART_Track" Height="10" IsDirectionReversed="False" Width="Auto">
|
||||
<Track.Thumb>
|
||||
<Thumb Template="{StaticResource Thumb}" Focusable="False" Margin="2.5" OverridesDefaultStyle="True" SnapsToDevicePixels="True" />
|
||||
</Track.Thumb>
|
||||
</Track>
|
||||
</ControlTemplate>
|
||||
<ControlTemplate x:Key="SmallBarScrollViewer" TargetType="{x:Type ScrollViewer}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<ScrollContentPresenter Grid.Column="0" Grid.Row="0" />
|
||||
<ScrollBar Name="PART_VerticalScrollBar" Grid.Column="1" Grid.Row="0" OverridesDefaultStyle="True" Value="{TemplateBinding VerticalOffset}"
|
||||
Maximum="{TemplateBinding ScrollableHeight}" Template="{StaticResource VerticalScrollBar}"
|
||||
ViewportSize="{TemplateBinding ViewportHeight}" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" />
|
||||
<ScrollBar Name="PART_HorizontalScrollBar" Grid.Column="0" Grid.Row="1" OverridesDefaultStyle="True" Orientation="Horizontal"
|
||||
Value="{TemplateBinding HorizontalOffset}" Maximum="{TemplateBinding ScrollableWidth}" Template="{StaticResource HorizontalScrollBar}"
|
||||
ViewportSize="{TemplateBinding ViewportWidth}" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</ResourceDictionary>
|
|
@ -184,5 +184,23 @@ namespace SafeExamBrowser.WindowsApi.Constants
|
|||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms646287(v=vs.85).aspx
|
||||
/// </summary>
|
||||
internal const int WM_SYSKEYUP = 0x105;
|
||||
|
||||
/// <summary>
|
||||
/// Posted when the user presses the first or second X button while the cursor is in the client area of a window. If the mouse is
|
||||
/// not captured, the message is posted to the window beneath the cursor. Otherwise, the message is posted to the window that has
|
||||
/// captured the mouse.
|
||||
///
|
||||
/// See https://docs.microsoft.com/de-de/windows/desktop/inputdev/wm-xbuttondown.
|
||||
/// </summary>
|
||||
internal const int WM_XBUTTONDOWN = 0x20B;
|
||||
|
||||
/// <summary>
|
||||
/// Posted when the user releases the first or second X button while the cursor is in the client area of a window. If the mouse is
|
||||
/// not captured, the message is posted to the window beneath the cursor. Otherwise, the message is posted to the window that has
|
||||
/// captured the mouse.
|
||||
///
|
||||
/// See https://docs.microsoft.com/de-de/windows/desktop/inputdev/wm-xbuttonup.
|
||||
/// </summary>
|
||||
internal const int WM_XBUTTONUP = 0x20C;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ namespace SafeExamBrowser.WindowsApi.Monitoring
|
|||
case Constant.WM_SYSKEYUP:
|
||||
return KeyState.Released;
|
||||
default:
|
||||
return KeyState.None;
|
||||
return KeyState.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -82,8 +82,11 @@ namespace SafeExamBrowser.WindowsApi.Monitoring
|
|||
case Constant.WM_RBUTTONDOWN:
|
||||
case Constant.WM_RBUTTONUP:
|
||||
return MouseButton.Right;
|
||||
case Constant.WM_XBUTTONDOWN:
|
||||
case Constant.WM_XBUTTONUP:
|
||||
return MouseButton.Auxiliary;
|
||||
default:
|
||||
return MouseButton.None;
|
||||
return MouseButton.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,13 +97,15 @@ namespace SafeExamBrowser.WindowsApi.Monitoring
|
|||
case Constant.WM_LBUTTONDOWN:
|
||||
case Constant.WM_MBUTTONDOWN:
|
||||
case Constant.WM_RBUTTONDOWN:
|
||||
case Constant.WM_XBUTTONDOWN:
|
||||
return KeyState.Pressed;
|
||||
case Constant.WM_LBUTTONUP:
|
||||
case Constant.WM_MBUTTONUP:
|
||||
case Constant.WM_RBUTTONUP:
|
||||
case Constant.WM_XBUTTONUP:
|
||||
return KeyState.Released;
|
||||
default:
|
||||
return KeyState.None;
|
||||
return KeyState.Unknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue