SEBWIN-308: Implemented basic download overview for browser.
This commit is contained in:
parent
b9536c6a1b
commit
97f3fb4a02
30 changed files with 582 additions and 133 deletions
|
@ -23,6 +23,7 @@ using SafeExamBrowser.Logging.Contracts;
|
||||||
using SafeExamBrowser.Settings.Browser.Proxy;
|
using SafeExamBrowser.Settings.Browser.Proxy;
|
||||||
using SafeExamBrowser.Settings.Logging;
|
using SafeExamBrowser.Settings.Logging;
|
||||||
using SafeExamBrowser.UserInterface.Contracts;
|
using SafeExamBrowser.UserInterface.Contracts;
|
||||||
|
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
||||||
using BrowserSettings = SafeExamBrowser.Settings.Browser.BrowserSettings;
|
using BrowserSettings = SafeExamBrowser.Settings.Browser.BrowserSettings;
|
||||||
|
|
||||||
|
@ -34,6 +35,7 @@ namespace SafeExamBrowser.Browser
|
||||||
|
|
||||||
private AppConfig appConfig;
|
private AppConfig appConfig;
|
||||||
private List<BrowserApplicationInstance> instances;
|
private List<BrowserApplicationInstance> instances;
|
||||||
|
private IFileSystemDialog fileSystemDialog;
|
||||||
private IMessageBox messageBox;
|
private IMessageBox messageBox;
|
||||||
private IModuleLogger logger;
|
private IModuleLogger logger;
|
||||||
private BrowserSettings settings;
|
private BrowserSettings settings;
|
||||||
|
@ -53,12 +55,14 @@ namespace SafeExamBrowser.Browser
|
||||||
public BrowserApplication(
|
public BrowserApplication(
|
||||||
AppConfig appConfig,
|
AppConfig appConfig,
|
||||||
BrowserSettings settings,
|
BrowserSettings settings,
|
||||||
|
IFileSystemDialog fileSystemDialog,
|
||||||
IMessageBox messageBox,
|
IMessageBox messageBox,
|
||||||
IModuleLogger logger,
|
IModuleLogger logger,
|
||||||
IText text,
|
IText text,
|
||||||
IUserInterfaceFactory uiFactory)
|
IUserInterfaceFactory uiFactory)
|
||||||
{
|
{
|
||||||
this.appConfig = appConfig;
|
this.appConfig = appConfig;
|
||||||
|
this.fileSystemDialog = fileSystemDialog;
|
||||||
this.instances = new List<BrowserApplicationInstance>();
|
this.instances = new List<BrowserApplicationInstance>();
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.messageBox = messageBox;
|
this.messageBox = messageBox;
|
||||||
|
@ -122,7 +126,7 @@ namespace SafeExamBrowser.Browser
|
||||||
var isMainInstance = instances.Count == 0;
|
var isMainInstance = instances.Count == 0;
|
||||||
var instanceLogger = logger.CloneFor($"Browser Instance #{id}");
|
var instanceLogger = logger.CloneFor($"Browser Instance #{id}");
|
||||||
var startUrl = url ?? settings.StartUrl;
|
var startUrl = url ?? settings.StartUrl;
|
||||||
var instance = new BrowserApplicationInstance(appConfig, settings, id, isMainInstance, messageBox, instanceLogger, text, uiFactory, startUrl);
|
var instance = new BrowserApplicationInstance(appConfig, settings, id, isMainInstance, fileSystemDialog, messageBox, instanceLogger, text, uiFactory, startUrl);
|
||||||
|
|
||||||
instance.ConfigurationDownloadRequested += (fileName, args) => ConfigurationDownloadRequested?.Invoke(fileName, args);
|
instance.ConfigurationDownloadRequested += (fileName, args) => ConfigurationDownloadRequested?.Invoke(fileName, args);
|
||||||
instance.PopupRequested += Instance_PopupRequested;
|
instance.PopupRequested += Instance_PopupRequested;
|
||||||
|
|
|
@ -24,6 +24,8 @@ using SafeExamBrowser.Settings.Browser;
|
||||||
using SafeExamBrowser.Settings.Browser.Filter;
|
using SafeExamBrowser.Settings.Browser.Filter;
|
||||||
using SafeExamBrowser.UserInterface.Contracts;
|
using SafeExamBrowser.UserInterface.Contracts;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Browser;
|
using SafeExamBrowser.UserInterface.Contracts.Browser;
|
||||||
|
using SafeExamBrowser.UserInterface.Contracts.Browser.Data;
|
||||||
|
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Browser
|
namespace SafeExamBrowser.Browser
|
||||||
|
@ -37,6 +39,7 @@ namespace SafeExamBrowser.Browser
|
||||||
private IBrowserWindow window;
|
private IBrowserWindow window;
|
||||||
private HttpClient httpClient;
|
private HttpClient httpClient;
|
||||||
private bool isMainInstance;
|
private bool isMainInstance;
|
||||||
|
private IFileSystemDialog fileSystemDialog;
|
||||||
private IMessageBox messageBox;
|
private IMessageBox messageBox;
|
||||||
private IModuleLogger logger;
|
private IModuleLogger logger;
|
||||||
private BrowserSettings settings;
|
private BrowserSettings settings;
|
||||||
|
@ -69,6 +72,7 @@ namespace SafeExamBrowser.Browser
|
||||||
BrowserSettings settings,
|
BrowserSettings settings,
|
||||||
int id,
|
int id,
|
||||||
bool isMainInstance,
|
bool isMainInstance,
|
||||||
|
IFileSystemDialog fileSystemDialog,
|
||||||
IMessageBox messageBox,
|
IMessageBox messageBox,
|
||||||
IModuleLogger logger,
|
IModuleLogger logger,
|
||||||
IText text,
|
IText text,
|
||||||
|
@ -79,6 +83,7 @@ namespace SafeExamBrowser.Browser
|
||||||
this.Id = id;
|
this.Id = id;
|
||||||
this.httpClient = new HttpClient();
|
this.httpClient = new HttpClient();
|
||||||
this.isMainInstance = isMainInstance;
|
this.isMainInstance = isMainInstance;
|
||||||
|
this.fileSystemDialog = fileSystemDialog;
|
||||||
this.messageBox = messageBox;
|
this.messageBox = messageBox;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
@ -122,6 +127,7 @@ namespace SafeExamBrowser.Browser
|
||||||
displayHandler.FaviconChanged += DisplayHandler_FaviconChanged;
|
displayHandler.FaviconChanged += DisplayHandler_FaviconChanged;
|
||||||
displayHandler.ProgressChanged += DisplayHandler_ProgressChanged;
|
displayHandler.ProgressChanged += DisplayHandler_ProgressChanged;
|
||||||
downloadHandler.ConfigurationDownloadRequested += DownloadHandler_ConfigurationDownloadRequested;
|
downloadHandler.ConfigurationDownloadRequested += DownloadHandler_ConfigurationDownloadRequested;
|
||||||
|
downloadHandler.DownloadUpdated += DownloadHandler_DownloadUpdated;
|
||||||
keyboardHandler.ReloadRequested += ReloadRequested;
|
keyboardHandler.ReloadRequested += ReloadRequested;
|
||||||
keyboardHandler.ZoomInRequested += ZoomInRequested;
|
keyboardHandler.ZoomInRequested += ZoomInRequested;
|
||||||
keyboardHandler.ZoomOutRequested += ZoomOutRequested;
|
keyboardHandler.ZoomOutRequested += ZoomOutRequested;
|
||||||
|
@ -211,8 +217,7 @@ namespace SafeExamBrowser.Browser
|
||||||
|
|
||||||
private void DialogHandler_DialogRequested(DialogRequestedEventArgs args)
|
private void DialogHandler_DialogRequested(DialogRequestedEventArgs args)
|
||||||
{
|
{
|
||||||
var dialog = uiFactory.CreateFileSystemDialog(args.Element, args.InitialPath, args.Operation, title: args.Title);
|
var result = fileSystemDialog.Show(args.Element, args.InitialPath, args.Operation, title: args.Title, owner: window);
|
||||||
var result = dialog.Show(window);
|
|
||||||
|
|
||||||
if (result.Success)
|
if (result.Success)
|
||||||
{
|
{
|
||||||
|
@ -269,6 +274,11 @@ namespace SafeExamBrowser.Browser
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DownloadHandler_DownloadUpdated(DownloadItemState state)
|
||||||
|
{
|
||||||
|
window.UpdateDownloadState(state);
|
||||||
|
}
|
||||||
|
|
||||||
private void LifeSpanHandler_PopupRequested(PopupRequestedEventArgs args)
|
private void LifeSpanHandler_PopupRequested(PopupRequestedEventArgs args)
|
||||||
{
|
{
|
||||||
var validCurrentUri = Uri.TryCreate(control.Address, UriKind.Absolute, out var currentUri);
|
var validCurrentUri = Uri.TryCreate(control.Address, UriKind.Absolute, out var currentUri);
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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 SafeExamBrowser.UserInterface.Contracts.Browser.Data;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Browser.Events
|
||||||
|
{
|
||||||
|
internal delegate void DownloadUpdatedEventHandler(DownloadItemState state);
|
||||||
|
}
|
|
@ -12,8 +12,10 @@ using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
using SafeExamBrowser.Browser.Contracts.Events;
|
using SafeExamBrowser.Browser.Contracts.Events;
|
||||||
|
using SafeExamBrowser.Browser.Events;
|
||||||
using SafeExamBrowser.Configuration.Contracts;
|
using SafeExamBrowser.Configuration.Contracts;
|
||||||
using SafeExamBrowser.Logging.Contracts;
|
using SafeExamBrowser.Logging.Contracts;
|
||||||
|
using SafeExamBrowser.UserInterface.Contracts.Browser.Data;
|
||||||
using Syroot.Windows.IO;
|
using Syroot.Windows.IO;
|
||||||
using BrowserSettings = SafeExamBrowser.Settings.Browser.BrowserSettings;
|
using BrowserSettings = SafeExamBrowser.Settings.Browser.BrowserSettings;
|
||||||
|
|
||||||
|
@ -24,14 +26,17 @@ namespace SafeExamBrowser.Browser.Handlers
|
||||||
private AppConfig appConfig;
|
private AppConfig appConfig;
|
||||||
private BrowserSettings settings;
|
private BrowserSettings settings;
|
||||||
private ConcurrentDictionary<int, DownloadFinishedCallback> callbacks;
|
private ConcurrentDictionary<int, DownloadFinishedCallback> callbacks;
|
||||||
|
private ConcurrentDictionary<int, Guid> downloads;
|
||||||
private ILogger logger;
|
private ILogger logger;
|
||||||
|
|
||||||
internal event DownloadRequestedEventHandler ConfigurationDownloadRequested;
|
internal event DownloadRequestedEventHandler ConfigurationDownloadRequested;
|
||||||
|
internal event DownloadUpdatedEventHandler DownloadUpdated;
|
||||||
|
|
||||||
internal DownloadHandler(AppConfig appConfig, BrowserSettings settings, ILogger logger)
|
internal DownloadHandler(AppConfig appConfig, BrowserSettings settings, ILogger logger)
|
||||||
{
|
{
|
||||||
this.appConfig = appConfig;
|
this.appConfig = appConfig;
|
||||||
this.callbacks = new ConcurrentDictionary<int, DownloadFinishedCallback>();
|
this.callbacks = new ConcurrentDictionary<int, DownloadFinishedCallback>();
|
||||||
|
this.downloads = new ConcurrentDictionary<int, Guid>();
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
}
|
}
|
||||||
|
@ -60,18 +65,35 @@ namespace SafeExamBrowser.Browser.Handlers
|
||||||
|
|
||||||
public void OnDownloadUpdated(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback)
|
public void OnDownloadUpdated(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback)
|
||||||
{
|
{
|
||||||
// TODO: Show download progress in respective window -> event for BrowserApplicationInstance!
|
var hasId = downloads.TryGetValue(downloadItem.Id, out var id);
|
||||||
|
|
||||||
|
if (hasId)
|
||||||
|
{
|
||||||
|
var state = new DownloadItemState(id)
|
||||||
|
{
|
||||||
|
Completion = downloadItem.PercentComplete / 100.0,
|
||||||
|
FullPath = downloadItem.FullPath,
|
||||||
|
IsCancelled = downloadItem.IsCancelled,
|
||||||
|
IsComplete = downloadItem.IsComplete,
|
||||||
|
Url = downloadItem.Url
|
||||||
|
};
|
||||||
|
|
||||||
|
Task.Run(() => DownloadUpdated?.Invoke(state));
|
||||||
|
}
|
||||||
|
|
||||||
if (downloadItem.IsComplete || downloadItem.IsCancelled)
|
if (downloadItem.IsComplete || downloadItem.IsCancelled)
|
||||||
{
|
{
|
||||||
|
logger.Debug($"Download of '{downloadItem.Url}' {(downloadItem.IsComplete ? "is complete" : "was cancelled")}.");
|
||||||
|
|
||||||
if (callbacks.TryRemove(downloadItem.Id, out DownloadFinishedCallback finished) && finished != null)
|
if (callbacks.TryRemove(downloadItem.Id, out DownloadFinishedCallback finished) && finished != null)
|
||||||
{
|
{
|
||||||
Task.Run(() => finished.Invoke(downloadItem.IsComplete, downloadItem.FullPath));
|
Task.Run(() => finished.Invoke(downloadItem.IsComplete, downloadItem.FullPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Debug($"Download of '{downloadItem.Url}' {(downloadItem.IsComplete ? "is complete" : "was cancelled")}.");
|
if (hasId)
|
||||||
|
{
|
||||||
// TODO: Show success message or download icon like Firefox in respective window!
|
downloads.TryRemove(downloadItem.Id, out _);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,6 +122,8 @@ namespace SafeExamBrowser.Browser.Handlers
|
||||||
logger.Debug($"Automatically downloading file as '{filePath}'.");
|
logger.Debug($"Automatically downloading file as '{filePath}'.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
downloads[downloadItem.Id] = Guid.NewGuid();
|
||||||
|
|
||||||
using (callback)
|
using (callback)
|
||||||
{
|
{
|
||||||
callback.Continue(filePath, showDialog);
|
callback.Continue(filePath, showDialog);
|
||||||
|
|
|
@ -68,6 +68,7 @@
|
||||||
<Compile Include="BrowserApplicationInstance.cs" />
|
<Compile Include="BrowserApplicationInstance.cs" />
|
||||||
<Compile Include="Events\DialogRequestedEventArgs.cs" />
|
<Compile Include="Events\DialogRequestedEventArgs.cs" />
|
||||||
<Compile Include="Events\DialogRequestedEventHandler.cs" />
|
<Compile Include="Events\DialogRequestedEventHandler.cs" />
|
||||||
|
<Compile Include="Events\DownloadUpdatedEventHandler.cs" />
|
||||||
<Compile Include="Events\FaviconChangedEventHandler.cs" />
|
<Compile Include="Events\FaviconChangedEventHandler.cs" />
|
||||||
<Compile Include="Events\InstanceTerminatedEventHandler.cs" />
|
<Compile Include="Events\InstanceTerminatedEventHandler.cs" />
|
||||||
<Compile Include="Events\PopupRequestedEventArgs.cs" />
|
<Compile Include="Events\PopupRequestedEventArgs.cs" />
|
||||||
|
|
|
@ -42,6 +42,7 @@ using SafeExamBrowser.SystemComponents.Keyboard;
|
||||||
using SafeExamBrowser.SystemComponents.PowerSupply;
|
using SafeExamBrowser.SystemComponents.PowerSupply;
|
||||||
using SafeExamBrowser.SystemComponents.WirelessNetwork;
|
using SafeExamBrowser.SystemComponents.WirelessNetwork;
|
||||||
using SafeExamBrowser.UserInterface.Contracts;
|
using SafeExamBrowser.UserInterface.Contracts;
|
||||||
|
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||||
using SafeExamBrowser.UserInterface.Shared.Activators;
|
using SafeExamBrowser.UserInterface.Shared.Activators;
|
||||||
|
@ -197,8 +198,9 @@ namespace SafeExamBrowser.Client
|
||||||
|
|
||||||
private IOperation BuildBrowserOperation()
|
private IOperation BuildBrowserOperation()
|
||||||
{
|
{
|
||||||
|
var fileSystemDialog = BuildFileSystemDialog();
|
||||||
var moduleLogger = ModuleLogger(nameof(BrowserApplication));
|
var moduleLogger = ModuleLogger(nameof(BrowserApplication));
|
||||||
var browser = new BrowserApplication(context.AppConfig, context.Settings.Browser, messageBox, moduleLogger, text, uiFactory);
|
var browser = new BrowserApplication(context.AppConfig, context.Settings.Browser, fileSystemDialog, messageBox, moduleLogger, text, uiFactory);
|
||||||
var operation = new BrowserOperation(actionCenter, context, logger, taskbar, taskview, uiFactory);
|
var operation = new BrowserOperation(actionCenter, context, logger, taskbar, taskview, uiFactory);
|
||||||
|
|
||||||
context.Browser = browser;
|
context.Browser = browser;
|
||||||
|
@ -282,6 +284,17 @@ namespace SafeExamBrowser.Client
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IFileSystemDialog BuildFileSystemDialog()
|
||||||
|
{
|
||||||
|
switch (uiMode)
|
||||||
|
{
|
||||||
|
case UserInterfaceMode.Mobile:
|
||||||
|
return new Mobile.FileSystemDialogFactory(text);
|
||||||
|
default:
|
||||||
|
return new Desktop.FileSystemDialogFactory(text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private IMessageBox BuildMessageBox()
|
private IMessageBox BuildMessageBox()
|
||||||
{
|
{
|
||||||
switch (uiMode)
|
switch (uiMode)
|
||||||
|
|
|
@ -22,6 +22,9 @@ namespace SafeExamBrowser.I18n.Contracts
|
||||||
Browser_Name,
|
Browser_Name,
|
||||||
Browser_Tooltip,
|
Browser_Tooltip,
|
||||||
BrowserWindow_DeveloperConsoleMenuItem,
|
BrowserWindow_DeveloperConsoleMenuItem,
|
||||||
|
BrowserWindow_Downloading,
|
||||||
|
BrowserWindow_DownloadCancelled,
|
||||||
|
BrowserWindow_DownloadComplete,
|
||||||
BrowserWindow_ZoomMenuItem,
|
BrowserWindow_ZoomMenuItem,
|
||||||
Build,
|
Build,
|
||||||
FileSystemDialog_Cancel,
|
FileSystemDialog_Cancel,
|
||||||
|
|
|
@ -24,6 +24,15 @@
|
||||||
<Entry key="BrowserWindow_DeveloperConsoleMenuItem">
|
<Entry key="BrowserWindow_DeveloperConsoleMenuItem">
|
||||||
Developer Console
|
Developer Console
|
||||||
</Entry>
|
</Entry>
|
||||||
|
<Entry key="BrowserWindow_Downloading">
|
||||||
|
Downloading...
|
||||||
|
</Entry>
|
||||||
|
<Entry key="BrowserWindow_DownloadCancelled">
|
||||||
|
Cancelled.
|
||||||
|
</Entry>
|
||||||
|
<Entry key="BrowserWindow_DownloadComplete">
|
||||||
|
Downloaded.
|
||||||
|
</Entry>
|
||||||
<Entry key="BrowserWindow_ZoomMenuItem">
|
<Entry key="BrowserWindow_ZoomMenuItem">
|
||||||
Page Zoom
|
Page Zoom
|
||||||
</Entry>
|
</Entry>
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.UserInterface.Contracts.Browser.Data
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the state of a download item.
|
||||||
|
/// </summary>
|
||||||
|
public class DownloadItemState
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The current completion of the item, as percentage value from <c>0.0</c> to <c>1.0</c>.
|
||||||
|
/// </summary>
|
||||||
|
public double Completion { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The full path of the download location for the item.
|
||||||
|
/// </summary>
|
||||||
|
public string FullPath { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The unique identifier of the item.
|
||||||
|
/// </summary>
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that the download was cancelled.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCancelled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that the download was completed.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsComplete { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The download URL of the item.
|
||||||
|
/// </summary>
|
||||||
|
public string Url { get; set; }
|
||||||
|
|
||||||
|
public DownloadItemState(Guid id)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using SafeExamBrowser.Applications.Contracts.Resources.Icons;
|
using SafeExamBrowser.Applications.Contracts.Resources.Icons;
|
||||||
|
using SafeExamBrowser.UserInterface.Contracts.Browser.Data;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Browser.Events;
|
using SafeExamBrowser.UserInterface.Contracts.Browser.Events;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||||
|
|
||||||
|
@ -83,6 +84,11 @@ namespace SafeExamBrowser.UserInterface.Contracts.Browser
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void UpdateIcon(IconResource icon);
|
void UpdateIcon(IconResource icon);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the download state for the given item.
|
||||||
|
/// </summary>
|
||||||
|
void UpdateDownloadState(DownloadItemState state);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the loading state according to the given value.
|
/// Updates the loading state according to the given value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -13,8 +13,8 @@ namespace SafeExamBrowser.UserInterface.Contracts.FileSystemDialog
|
||||||
public interface IFileSystemDialog
|
public interface IFileSystemDialog
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Shows the dialog to the user. If a parent window is specified, the dialog is rendered modally for the given parent.
|
/// Creates a dialog according to the given parameters and shows it to the user.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
FileSystemDialogResult Show(IWindow parent = null);
|
FileSystemDialogResult Show(FileSystemElement element, string initialPath, FileSystemOperation operation, string message = default(string), string title = default(string), IWindow owner = default(IWindow));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,6 @@ using SafeExamBrowser.SystemComponents.Contracts.Keyboard;
|
||||||
using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
|
using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
|
||||||
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
|
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Browser;
|
using SafeExamBrowser.UserInterface.Contracts.Browser;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
|
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
|
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
|
||||||
|
@ -50,11 +49,6 @@ namespace SafeExamBrowser.UserInterface.Contracts
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IBrowserWindow CreateBrowserWindow(IBrowserControl control, BrowserSettings settings, bool isMainWindow);
|
IBrowserWindow CreateBrowserWindow(IBrowserControl control, BrowserSettings settings, bool isMainWindow);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a file system dialog according to the given parameters.
|
|
||||||
/// </summary>
|
|
||||||
IFileSystemDialog CreateFileSystemDialog(FileSystemElement element, string initialPath, FileSystemOperation operation, string message = default(string), string title = default(string));
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a folder dialog with the given message.
|
/// Creates a folder dialog with the given message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="Browser\Data\DownloadItemState.cs" />
|
||||||
<Compile Include="Browser\Events\AddressChangedEventHandler.cs" />
|
<Compile Include="Browser\Events\AddressChangedEventHandler.cs" />
|
||||||
<Compile Include="Browser\Events\LoadingStateChangedEventHandler.cs" />
|
<Compile Include="Browser\Events\LoadingStateChangedEventHandler.cs" />
|
||||||
<Compile Include="Browser\Events\TitleChangedEventHandler.cs" />
|
<Compile Include="Browser\Events\TitleChangedEventHandler.cs" />
|
||||||
|
|
|
@ -33,12 +33,21 @@
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Button Grid.Column="0" x:Name="BackwardButton" Height="30" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
|
<Button Grid.Column="0" x:Name="BackwardButton" 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="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" />
|
<Button Grid.Column="2" x:Name="ReloadButton" Height="30" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
|
||||||
<TextBox Grid.Column="3" x:Name="UrlTextBox" Height="25" HorizontalAlignment="Stretch" Margin="5,0" Padding="5,0" VerticalContentAlignment="Center" />
|
<TextBox Grid.Column="3" x:Name="UrlTextBox" Height="25" HorizontalAlignment="Stretch" Margin="5,0" Padding="5,0" VerticalContentAlignment="Center" />
|
||||||
<Button Grid.Column="4" x:Name="MenuButton" Height="30" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
|
<Button Grid.Column="4" x:Name="DownloadsButton" Height="30" HorizontalAlignment="Center" Margin="5" Padding="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" Visibility="Collapsed">
|
||||||
|
<fa:ImageAwesome Icon="Download" />
|
||||||
|
</Button>
|
||||||
|
<Popup x:Name="DownloadsPopup" AllowsTransparency="True" PopupAnimation="Slide" Placement="Custom" PlacementTarget="{Binding ElementName=BrowserControlHost}">
|
||||||
|
<Border Background="{StaticResource BackgroundBrush}" BorderBrush="LightGray" BorderThickness="1,0,1,1" MinWidth="250">
|
||||||
|
<StackPanel x:Name="Downloads" Orientation="Vertical" />
|
||||||
|
</Border>
|
||||||
|
</Popup>
|
||||||
|
<Button Grid.Column="5" x:Name="MenuButton" Height="30" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
|
||||||
<Popup x:Name="MenuPopup" IsOpen="False" AllowsTransparency="True" PopupAnimation="Slide" Placement="Custom" PlacementTarget="{Binding ElementName=BrowserControlHost}">
|
<Popup x:Name="MenuPopup" IsOpen="False" AllowsTransparency="True" PopupAnimation="Slide" Placement="Custom" PlacementTarget="{Binding ElementName=BrowserControlHost}">
|
||||||
<Border Background="{StaticResource BackgroundBrush}" BorderBrush="LightGray" BorderThickness="1,0,1,1" Width="250">
|
<Border Background="{StaticResource BackgroundBrush}" BorderBrush="LightGray" BorderThickness="1,0,1,1" Width="250">
|
||||||
<StackPanel Orientation="Vertical">
|
<StackPanel Orientation="Vertical">
|
||||||
|
|
|
@ -13,16 +13,17 @@ using System.Windows;
|
||||||
using System.Windows.Controls.Primitives;
|
using System.Windows.Controls.Primitives;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Interop;
|
using System.Windows.Interop;
|
||||||
using System.Windows.Media;
|
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
using SafeExamBrowser.Applications.Contracts.Resources.Icons;
|
using SafeExamBrowser.Applications.Contracts.Resources.Icons;
|
||||||
using SafeExamBrowser.I18n.Contracts;
|
using SafeExamBrowser.I18n.Contracts;
|
||||||
using SafeExamBrowser.Settings.Browser;
|
using SafeExamBrowser.Settings.Browser;
|
||||||
using SafeExamBrowser.UserInterface.Contracts;
|
using SafeExamBrowser.UserInterface.Contracts;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Browser;
|
using SafeExamBrowser.UserInterface.Contracts.Browser;
|
||||||
|
using SafeExamBrowser.UserInterface.Contracts.Browser.Data;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Browser.Events;
|
using SafeExamBrowser.UserInterface.Contracts.Browser.Events;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Events;
|
using SafeExamBrowser.UserInterface.Contracts.Windows.Events;
|
||||||
|
using SafeExamBrowser.UserInterface.Desktop.Controls.Browser;
|
||||||
using SafeExamBrowser.UserInterface.Shared.Utilities;
|
using SafeExamBrowser.UserInterface.Shared.Utilities;
|
||||||
|
|
||||||
namespace SafeExamBrowser.UserInterface.Desktop
|
namespace SafeExamBrowser.UserInterface.Desktop
|
||||||
|
@ -117,6 +118,36 @@ namespace SafeExamBrowser.UserInterface.Desktop
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateDownloadState(DownloadItemState state)
|
||||||
|
{
|
||||||
|
Dispatcher.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
var isNewItem = true;
|
||||||
|
|
||||||
|
foreach (var child in Downloads.Children)
|
||||||
|
{
|
||||||
|
if (child is DownloadItemControl control && control.Id == state.Id)
|
||||||
|
{
|
||||||
|
control.Update(state);
|
||||||
|
isNewItem = false;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNewItem)
|
||||||
|
{
|
||||||
|
var control = new DownloadItemControl(state.Id, text);
|
||||||
|
|
||||||
|
control.Update(state);
|
||||||
|
Downloads.Children.Add(control);
|
||||||
|
}
|
||||||
|
|
||||||
|
DownloadsButton.Visibility = Visibility.Visible;
|
||||||
|
DownloadsPopup.IsOpen = IsActive;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void UpdateLoadingState(bool isLoading)
|
public void UpdateLoadingState(bool isLoading)
|
||||||
{
|
{
|
||||||
Dispatcher.Invoke(() => ProgressBar.Visibility = isLoading ? Visibility.Visible : Visibility.Hidden);
|
Dispatcher.Invoke(() => ProgressBar.Visibility = isLoading ? Visibility.Visible : Visibility.Hidden);
|
||||||
|
@ -167,7 +198,7 @@ namespace SafeExamBrowser.UserInterface.Desktop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private CustomPopupPlacement[] MenuPopup_PlacementCallback(Size popupSize, Size targetSize, Point offset)
|
private CustomPopupPlacement[] Popup_PlacementCallback(Size popupSize, Size targetSize, Point offset)
|
||||||
{
|
{
|
||||||
return new[]
|
return new[]
|
||||||
{
|
{
|
||||||
|
@ -216,21 +247,23 @@ namespace SafeExamBrowser.UserInterface.Desktop
|
||||||
|
|
||||||
private void RegisterEvents()
|
private void RegisterEvents()
|
||||||
{
|
{
|
||||||
var originalBrush = MenuButton.Background;
|
|
||||||
|
|
||||||
BackwardButton.Click += (o, args) => BackwardNavigationRequested?.Invoke();
|
BackwardButton.Click += (o, args) => BackwardNavigationRequested?.Invoke();
|
||||||
Closing += BrowserWindow_Closing;
|
Closing += BrowserWindow_Closing;
|
||||||
DeveloperConsoleButton.Click += (o, args) => DeveloperConsoleRequested?.Invoke();
|
DeveloperConsoleButton.Click += (o, args) => DeveloperConsoleRequested?.Invoke();
|
||||||
|
DownloadsButton.Click += (o, args) => DownloadsPopup.IsOpen = !DownloadsPopup.IsOpen;
|
||||||
|
DownloadsButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => DownloadsPopup.IsOpen = DownloadsPopup.IsMouseOver));
|
||||||
|
DownloadsPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
|
||||||
|
DownloadsPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => DownloadsPopup.IsOpen = DownloadsPopup.IsMouseOver));
|
||||||
ForwardButton.Click += (o, args) => ForwardNavigationRequested?.Invoke();
|
ForwardButton.Click += (o, args) => ForwardNavigationRequested?.Invoke();
|
||||||
Loaded += BrowserWindow_Loaded;
|
Loaded += BrowserWindow_Loaded;
|
||||||
MenuButton.Click += (o, args) => MenuPopup.IsOpen = !MenuPopup.IsOpen;
|
MenuButton.Click += (o, args) => MenuPopup.IsOpen = !MenuPopup.IsOpen;
|
||||||
MenuButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = MenuPopup.IsMouseOver));
|
MenuButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = MenuPopup.IsMouseOver));
|
||||||
MenuPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(MenuPopup_PlacementCallback);
|
MenuPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
|
||||||
MenuPopup.Closed += (o, args) => MenuButton.Background = originalBrush;
|
MenuPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = MenuPopup.IsMouseOver));
|
||||||
MenuPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = IsMouseOver));
|
|
||||||
MenuPopup.Opened += (o, args) => MenuButton.Background = Brushes.LightGray;
|
|
||||||
KeyUp += BrowserWindow_KeyUp;
|
KeyUp += BrowserWindow_KeyUp;
|
||||||
|
LocationChanged += (o, args) => { DownloadsPopup.IsOpen = false; MenuPopup.IsOpen = false; };
|
||||||
ReloadButton.Click += (o, args) => ReloadRequested?.Invoke();
|
ReloadButton.Click += (o, args) => ReloadRequested?.Invoke();
|
||||||
|
SizeChanged += (o, args) => { DownloadsPopup.IsOpen = false; MenuPopup.IsOpen = false; };
|
||||||
SystemParameters.StaticPropertyChanged += SystemParameters_StaticPropertyChanged;
|
SystemParameters.StaticPropertyChanged += SystemParameters_StaticPropertyChanged;
|
||||||
UrlTextBox.GotKeyboardFocus += (o, args) => UrlTextBox.SelectAll();
|
UrlTextBox.GotKeyboardFocus += (o, args) => UrlTextBox.SelectAll();
|
||||||
UrlTextBox.GotMouseCapture += UrlTextBox_GotMouseCapture;
|
UrlTextBox.GotMouseCapture += UrlTextBox_GotMouseCapture;
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
<UserControl x:Class="SafeExamBrowser.UserInterface.Desktop.Controls.Browser.DownloadItemControl"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop.Controls.Browser"
|
||||||
|
mc:Ignorable="d" d:DesignHeight="50" d:DesignWidth="200">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<ContentControl Grid.Row="0" Grid.RowSpan="2" Grid.Column="0" Panel.ZIndex="2" Name="Icon" Margin="10" Width="25" />
|
||||||
|
<ProgressBar Grid.Row="0" Grid.RowSpan="2" Grid.Column="0" Grid.ColumnSpan="2" Panel.ZIndex="1" Name="Progress" BorderThickness="0" />
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="1" Panel.ZIndex="2" Name="ItemName" FontWeight="Bold" Margin="0,10,10,0" />
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="1" Panel.ZIndex="2" Name="Status" FontStyle="Italic" Margin="0,0,10,10" />
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using SafeExamBrowser.I18n.Contracts;
|
||||||
|
using SafeExamBrowser.UserInterface.Contracts.Browser.Data;
|
||||||
|
using SafeExamBrowser.UserInterface.Shared.Utilities;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.UserInterface.Desktop.Controls.Browser
|
||||||
|
{
|
||||||
|
public partial class DownloadItemControl : UserControl
|
||||||
|
{
|
||||||
|
private IText text;
|
||||||
|
|
||||||
|
public Guid Id { get; }
|
||||||
|
|
||||||
|
public DownloadItemControl(Guid id, IText text)
|
||||||
|
{
|
||||||
|
this.Id = id;
|
||||||
|
this.text = text;
|
||||||
|
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(DownloadItemState state)
|
||||||
|
{
|
||||||
|
ItemName.Text = Uri.TryCreate(state.Url, UriKind.Absolute, out var uri) ? Path.GetFileName(uri.AbsolutePath) : state.Url;
|
||||||
|
Progress.Value = state.Completion * 100;
|
||||||
|
Status.Text = $"{text.Get(TextKey.BrowserWindow_Downloading)} ({state.Completion * 100}%)";
|
||||||
|
|
||||||
|
if (File.Exists(state.FullPath))
|
||||||
|
{
|
||||||
|
ItemName.Text = Path.GetFileName(state.FullPath);
|
||||||
|
Icon.Content = new Image { Source = IconLoader.LoadIconFor(new FileInfo(state.FullPath)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.IsCancelled)
|
||||||
|
{
|
||||||
|
Progress.Visibility = System.Windows.Visibility.Collapsed;
|
||||||
|
Status.Text = text.Get(TextKey.BrowserWindow_DownloadCancelled);
|
||||||
|
}
|
||||||
|
else if (state.IsComplete)
|
||||||
|
{
|
||||||
|
Progress.Visibility = System.Windows.Visibility.Collapsed;
|
||||||
|
Status.Text = text.Get(TextKey.BrowserWindow_DownloadComplete);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,7 @@
|
||||||
xmlns:fa="http://schemas.fontawesome.io/icons/"
|
xmlns:fa="http://schemas.fontawesome.io/icons/"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop"
|
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop"
|
||||||
mc:Ignorable="d" Height="500" Width="750" ResizeMode="NoResize">
|
mc:Ignorable="d" Height="500" Width="750" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
|
||||||
<Window.Resources>
|
<Window.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
|
|
@ -22,12 +22,13 @@ using SafeExamBrowser.UserInterface.Shared.Utilities;
|
||||||
|
|
||||||
namespace SafeExamBrowser.UserInterface.Desktop
|
namespace SafeExamBrowser.UserInterface.Desktop
|
||||||
{
|
{
|
||||||
public partial class FileSystemDialog : Window, IFileSystemDialog
|
public partial class FileSystemDialog : Window
|
||||||
{
|
{
|
||||||
private FileSystemElement element;
|
private FileSystemElement element;
|
||||||
private string initialPath;
|
private string initialPath;
|
||||||
private string message;
|
private string message;
|
||||||
private FileSystemOperation operation;
|
private FileSystemOperation operation;
|
||||||
|
private IWindow parent;
|
||||||
private IText text;
|
private IText text;
|
||||||
private string title;
|
private string title;
|
||||||
|
|
||||||
|
@ -37,12 +38,14 @@ namespace SafeExamBrowser.UserInterface.Desktop
|
||||||
FileSystemOperation operation,
|
FileSystemOperation operation,
|
||||||
IText text,
|
IText text,
|
||||||
string message = default(string),
|
string message = default(string),
|
||||||
string title = default(string))
|
string title = default(string),
|
||||||
|
IWindow parent = default(IWindow))
|
||||||
{
|
{
|
||||||
this.element = element;
|
this.element = element;
|
||||||
this.initialPath = initialPath;
|
this.initialPath = initialPath;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.operation = operation;
|
this.operation = operation;
|
||||||
|
this.parent = parent;
|
||||||
this.text = text;
|
this.text = text;
|
||||||
this.title = title;
|
this.title = title;
|
||||||
|
|
||||||
|
@ -50,26 +53,23 @@ namespace SafeExamBrowser.UserInterface.Desktop
|
||||||
InitializeDialog();
|
InitializeDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileSystemDialogResult Show(IWindow parent = null)
|
internal new FileSystemDialogResult Show()
|
||||||
{
|
{
|
||||||
return Dispatcher.Invoke(() =>
|
var result = new FileSystemDialogResult();
|
||||||
|
|
||||||
|
if (parent is Window)
|
||||||
{
|
{
|
||||||
var result = new FileSystemDialogResult();
|
Owner = parent as Window;
|
||||||
|
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||||
|
}
|
||||||
|
|
||||||
if (parent is Window)
|
if (ShowDialog() == true)
|
||||||
{
|
{
|
||||||
Owner = parent as Window;
|
result.FullPath = BuildFullPath();
|
||||||
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
result.Success = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ShowDialog() == true)
|
return result;
|
||||||
{
|
|
||||||
result.FullPath = BuildFullPath();
|
|
||||||
result.Success = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CancelButton_Click(object sender, RoutedEventArgs e)
|
private void CancelButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System.Windows;
|
||||||
|
using SafeExamBrowser.I18n.Contracts;
|
||||||
|
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
|
||||||
|
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.UserInterface.Desktop
|
||||||
|
{
|
||||||
|
public class FileSystemDialogFactory : IFileSystemDialog
|
||||||
|
{
|
||||||
|
private IText text;
|
||||||
|
|
||||||
|
public FileSystemDialogFactory(IText text)
|
||||||
|
{
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileSystemDialogResult Show(FileSystemElement element, string initialPath, FileSystemOperation operation, string message = null, string title = null, IWindow owner = null)
|
||||||
|
{
|
||||||
|
if (owner is Window window)
|
||||||
|
{
|
||||||
|
return window.Dispatcher.Invoke(() => new FileSystemDialog(element, initialPath, operation, text, message, title, owner).Show());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new FileSystemDialog(element, initialPath, operation, text, message, title).Show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -109,6 +109,9 @@
|
||||||
<Compile Include="Controls\ActionCenterWirelessNetworkControl.xaml.cs">
|
<Compile Include="Controls\ActionCenterWirelessNetworkControl.xaml.cs">
|
||||||
<DependentUpon>ActionCenterWirelessNetworkControl.xaml</DependentUpon>
|
<DependentUpon>ActionCenterWirelessNetworkControl.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Controls\Browser\DownloadItemControl.xaml.cs">
|
||||||
|
<DependentUpon>DownloadItemControl.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Controls\TaskbarApplicationControl.xaml.cs">
|
<Compile Include="Controls\TaskbarApplicationControl.xaml.cs">
|
||||||
<DependentUpon>TaskbarApplicationControl.xaml</DependentUpon>
|
<DependentUpon>TaskbarApplicationControl.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
@ -148,6 +151,7 @@
|
||||||
<Compile Include="FileSystemDialog.xaml.cs">
|
<Compile Include="FileSystemDialog.xaml.cs">
|
||||||
<DependentUpon>FileSystemDialog.xaml</DependentUpon>
|
<DependentUpon>FileSystemDialog.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="FileSystemDialogFactory.cs" />
|
||||||
<Compile Include="FolderDialog.cs" />
|
<Compile Include="FolderDialog.cs" />
|
||||||
<Compile Include="LockScreen.xaml.cs">
|
<Compile Include="LockScreen.xaml.cs">
|
||||||
<DependentUpon>LockScreen.xaml</DependentUpon>
|
<DependentUpon>LockScreen.xaml</DependentUpon>
|
||||||
|
@ -229,6 +233,10 @@
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
</Page>
|
</Page>
|
||||||
|
<Page Include="Controls\Browser\DownloadItemControl.xaml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
<Page Include="Controls\TaskbarApplicationControl.xaml">
|
<Page Include="Controls\TaskbarApplicationControl.xaml">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
|
|
@ -23,7 +23,6 @@ using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
|
||||||
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
|
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
|
||||||
using SafeExamBrowser.UserInterface.Contracts;
|
using SafeExamBrowser.UserInterface.Contracts;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Browser;
|
using SafeExamBrowser.UserInterface.Contracts.Browser;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
|
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
|
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
|
||||||
|
@ -76,11 +75,6 @@ namespace SafeExamBrowser.UserInterface.Desktop
|
||||||
return Application.Current.Dispatcher.Invoke(() => new BrowserWindow(control, settings, isMainWindow, text));
|
return Application.Current.Dispatcher.Invoke(() => new BrowserWindow(control, settings, isMainWindow, text));
|
||||||
}
|
}
|
||||||
|
|
||||||
public IFileSystemDialog CreateFileSystemDialog(FileSystemElement element, string initialPath, FileSystemOperation operation, string message = default(string), string title = default(string))
|
|
||||||
{
|
|
||||||
return Application.Current.Dispatcher.Invoke(() => new FileSystemDialog(element, initialPath, operation, text, message, title));
|
|
||||||
}
|
|
||||||
|
|
||||||
public IFolderDialog CreateFolderDialog(string message)
|
public IFolderDialog CreateFolderDialog(string message)
|
||||||
{
|
{
|
||||||
return new FolderDialog(message);
|
return new FolderDialog(message);
|
||||||
|
@ -105,26 +99,26 @@ namespace SafeExamBrowser.UserInterface.Desktop
|
||||||
|
|
||||||
public IWindow CreateLogWindow(ILogger logger)
|
public IWindow CreateLogWindow(ILogger logger)
|
||||||
{
|
{
|
||||||
LogWindow logWindow = null;
|
var window = default(LogWindow);
|
||||||
var logWindowReadyEvent = new AutoResetEvent(false);
|
var windowReadyEvent = new AutoResetEvent(false);
|
||||||
var logWindowThread = new Thread(() =>
|
var windowThread = new Thread(() =>
|
||||||
{
|
{
|
||||||
logWindow = new LogWindow(logger, text);
|
window = new LogWindow(logger, text);
|
||||||
logWindow.Closed += (o, args) => logWindow.Dispatcher.InvokeShutdown();
|
window.Closed += (o, args) => window.Dispatcher.InvokeShutdown();
|
||||||
logWindow.Show();
|
window.Show();
|
||||||
|
|
||||||
logWindowReadyEvent.Set();
|
windowReadyEvent.Set();
|
||||||
|
|
||||||
System.Windows.Threading.Dispatcher.Run();
|
System.Windows.Threading.Dispatcher.Run();
|
||||||
});
|
});
|
||||||
|
|
||||||
logWindowThread.SetApartmentState(ApartmentState.STA);
|
windowThread.SetApartmentState(ApartmentState.STA);
|
||||||
logWindowThread.IsBackground = true;
|
windowThread.IsBackground = true;
|
||||||
logWindowThread.Start();
|
windowThread.Start();
|
||||||
|
|
||||||
logWindowReadyEvent.WaitOne();
|
windowReadyEvent.WaitOne();
|
||||||
|
|
||||||
return logWindow;
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
public INotificationControl CreateNotificationControl(INotificationController controller, INotificationInfo info, Location location)
|
public INotificationControl CreateNotificationControl(INotificationController controller, INotificationInfo info, Location location)
|
||||||
|
@ -168,27 +162,26 @@ namespace SafeExamBrowser.UserInterface.Desktop
|
||||||
|
|
||||||
public ISplashScreen CreateSplashScreen(AppConfig appConfig = null)
|
public ISplashScreen CreateSplashScreen(AppConfig appConfig = null)
|
||||||
{
|
{
|
||||||
SplashScreen splashScreen = null;
|
var window = default(SplashScreen);
|
||||||
var splashReadyEvent = new AutoResetEvent(false);
|
var windowReadyEvent = new AutoResetEvent(false);
|
||||||
var splashScreenThread = new Thread(() =>
|
var windowThread = new Thread(() =>
|
||||||
{
|
{
|
||||||
splashScreen = new SplashScreen(text, appConfig);
|
window = new SplashScreen(text, appConfig);
|
||||||
splashScreen.Closed += (o, args) => splashScreen.Dispatcher.InvokeShutdown();
|
window.Closed += (o, args) => window.Dispatcher.InvokeShutdown();
|
||||||
splashScreen.Show();
|
window.Show();
|
||||||
|
|
||||||
splashReadyEvent.Set();
|
windowReadyEvent.Set();
|
||||||
|
|
||||||
System.Windows.Threading.Dispatcher.Run();
|
System.Windows.Threading.Dispatcher.Run();
|
||||||
});
|
});
|
||||||
|
|
||||||
splashScreenThread.SetApartmentState(ApartmentState.STA);
|
windowThread.SetApartmentState(ApartmentState.STA);
|
||||||
splashScreenThread.Name = nameof(SplashScreen);
|
windowThread.IsBackground = true;
|
||||||
splashScreenThread.IsBackground = true;
|
windowThread.Start();
|
||||||
splashScreenThread.Start();
|
|
||||||
|
|
||||||
splashReadyEvent.WaitOne();
|
windowReadyEvent.WaitOne();
|
||||||
|
|
||||||
return splashScreen;
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ISystemControl CreateWirelessNetworkControl(IWirelessAdapter wirelessAdapter, Location location)
|
public ISystemControl CreateWirelessNetworkControl(IWirelessAdapter wirelessAdapter, Location location)
|
||||||
|
|
|
@ -33,12 +33,21 @@
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition Width="Auto" />
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Button Grid.Column="0" x:Name="BackwardButton" Height="50" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
|
<Button Grid.Column="0" x:Name="BackwardButton" Height="50" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
|
||||||
<Button Grid.Column="1" x:Name="ForwardButton" Height="50" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
|
<Button Grid.Column="1" x:Name="ForwardButton" Height="50" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
|
||||||
<Button Grid.Column="2" x:Name="ReloadButton" Height="50" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
|
<Button Grid.Column="2" x:Name="ReloadButton" Height="50" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
|
||||||
<TextBox Grid.Column="3" x:Name="UrlTextBox" Height="50" HorizontalAlignment="Stretch" Margin="5,10" Padding="8" VerticalContentAlignment="Center" />
|
<TextBox Grid.Column="3" x:Name="UrlTextBox" Height="50" HorizontalAlignment="Stretch" Margin="5,10" Padding="8" VerticalContentAlignment="Center" />
|
||||||
<Button Grid.Column="4" x:Name="MenuButton" Height="50" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
|
<Button Grid.Column="4" x:Name="DownloadsButton" Height="50" HorizontalAlignment="Center" Margin="5" Padding="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" Visibility="Collapsed">
|
||||||
|
<fa:ImageAwesome Icon="Download" />
|
||||||
|
</Button>
|
||||||
|
<Popup x:Name="DownloadsPopup" AllowsTransparency="True" PopupAnimation="Slide" Placement="Custom" PlacementTarget="{Binding ElementName=BrowserControlHost}">
|
||||||
|
<Border Background="{StaticResource BackgroundBrush}" BorderBrush="LightGray" BorderThickness="1,0,1,1" MinWidth="250">
|
||||||
|
<StackPanel x:Name="Downloads" Orientation="Vertical" />
|
||||||
|
</Border>
|
||||||
|
</Popup>
|
||||||
|
<Button Grid.Column="5" x:Name="MenuButton" Height="50" HorizontalAlignment="Center" Margin="5" Template="{StaticResource BrowserButton}" VerticalAlignment="Center" />
|
||||||
<Popup x:Name="MenuPopup" IsOpen="False" AllowsTransparency="True" PopupAnimation="Slide" Placement="Custom" PlacementTarget="{Binding ElementName=BrowserControlHost}">
|
<Popup x:Name="MenuPopup" IsOpen="False" AllowsTransparency="True" PopupAnimation="Slide" Placement="Custom" PlacementTarget="{Binding ElementName=BrowserControlHost}">
|
||||||
<Border Background="{StaticResource BackgroundBrush}" BorderBrush="LightGray" BorderThickness="1,0,1,1" Width="350">
|
<Border Background="{StaticResource BackgroundBrush}" BorderBrush="LightGray" BorderThickness="1,0,1,1" Width="350">
|
||||||
<StackPanel Orientation="Vertical">
|
<StackPanel Orientation="Vertical">
|
||||||
|
|
|
@ -13,16 +13,17 @@ using System.Windows;
|
||||||
using System.Windows.Controls.Primitives;
|
using System.Windows.Controls.Primitives;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Interop;
|
using System.Windows.Interop;
|
||||||
using System.Windows.Media;
|
|
||||||
using System.Windows.Media.Imaging;
|
using System.Windows.Media.Imaging;
|
||||||
using SafeExamBrowser.Applications.Contracts.Resources.Icons;
|
using SafeExamBrowser.Applications.Contracts.Resources.Icons;
|
||||||
using SafeExamBrowser.I18n.Contracts;
|
using SafeExamBrowser.I18n.Contracts;
|
||||||
using SafeExamBrowser.Settings.Browser;
|
using SafeExamBrowser.Settings.Browser;
|
||||||
using SafeExamBrowser.UserInterface.Contracts;
|
using SafeExamBrowser.UserInterface.Contracts;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Browser;
|
using SafeExamBrowser.UserInterface.Contracts.Browser;
|
||||||
|
using SafeExamBrowser.UserInterface.Contracts.Browser.Data;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Browser.Events;
|
using SafeExamBrowser.UserInterface.Contracts.Browser.Events;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Events;
|
using SafeExamBrowser.UserInterface.Contracts.Windows.Events;
|
||||||
|
using SafeExamBrowser.UserInterface.Mobile.Controls.Browser;
|
||||||
using SafeExamBrowser.UserInterface.Shared.Utilities;
|
using SafeExamBrowser.UserInterface.Shared.Utilities;
|
||||||
|
|
||||||
namespace SafeExamBrowser.UserInterface.Mobile
|
namespace SafeExamBrowser.UserInterface.Mobile
|
||||||
|
@ -117,6 +118,36 @@ namespace SafeExamBrowser.UserInterface.Mobile
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateDownloadState(DownloadItemState state)
|
||||||
|
{
|
||||||
|
Dispatcher.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
var isNewItem = true;
|
||||||
|
|
||||||
|
foreach (var child in Downloads.Children)
|
||||||
|
{
|
||||||
|
if (child is DownloadItemControl control && control.Id == state.Id)
|
||||||
|
{
|
||||||
|
control.Update(state);
|
||||||
|
isNewItem = false;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNewItem)
|
||||||
|
{
|
||||||
|
var control = new DownloadItemControl(state.Id, text);
|
||||||
|
|
||||||
|
control.Update(state);
|
||||||
|
Downloads.Children.Add(control);
|
||||||
|
}
|
||||||
|
|
||||||
|
DownloadsButton.Visibility = Visibility.Visible;
|
||||||
|
DownloadsPopup.IsOpen = IsActive;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void UpdateLoadingState(bool isLoading)
|
public void UpdateLoadingState(bool isLoading)
|
||||||
{
|
{
|
||||||
Dispatcher.Invoke(() => ProgressBar.Visibility = isLoading ? Visibility.Visible : Visibility.Hidden);
|
Dispatcher.Invoke(() => ProgressBar.Visibility = isLoading ? Visibility.Visible : Visibility.Hidden);
|
||||||
|
@ -167,7 +198,7 @@ namespace SafeExamBrowser.UserInterface.Mobile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private CustomPopupPlacement[] MenuPopup_PlacementCallback(Size popupSize, Size targetSize, Point offset)
|
private CustomPopupPlacement[] Popup_PlacementCallback(Size popupSize, Size targetSize, Point offset)
|
||||||
{
|
{
|
||||||
return new[]
|
return new[]
|
||||||
{
|
{
|
||||||
|
@ -216,21 +247,23 @@ namespace SafeExamBrowser.UserInterface.Mobile
|
||||||
|
|
||||||
private void RegisterEvents()
|
private void RegisterEvents()
|
||||||
{
|
{
|
||||||
var originalBrush = MenuButton.Background;
|
|
||||||
|
|
||||||
BackwardButton.Click += (o, args) => BackwardNavigationRequested?.Invoke();
|
BackwardButton.Click += (o, args) => BackwardNavigationRequested?.Invoke();
|
||||||
Closing += BrowserWindow_Closing;
|
Closing += BrowserWindow_Closing;
|
||||||
DeveloperConsoleButton.Click += (o, args) => DeveloperConsoleRequested?.Invoke();
|
DeveloperConsoleButton.Click += (o, args) => DeveloperConsoleRequested?.Invoke();
|
||||||
|
DownloadsButton.Click += (o, args) => DownloadsPopup.IsOpen = !DownloadsPopup.IsOpen;
|
||||||
|
DownloadsButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => DownloadsPopup.IsOpen = DownloadsPopup.IsMouseOver));
|
||||||
|
DownloadsPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
|
||||||
|
DownloadsPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => DownloadsPopup.IsOpen = DownloadsPopup.IsMouseOver));
|
||||||
ForwardButton.Click += (o, args) => ForwardNavigationRequested?.Invoke();
|
ForwardButton.Click += (o, args) => ForwardNavigationRequested?.Invoke();
|
||||||
Loaded += BrowserWindow_Loaded;
|
Loaded += BrowserWindow_Loaded;
|
||||||
MenuButton.Click += (o, args) => MenuPopup.IsOpen = !MenuPopup.IsOpen;
|
MenuButton.Click += (o, args) => MenuPopup.IsOpen = !MenuPopup.IsOpen;
|
||||||
MenuButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = MenuPopup.IsMouseOver));
|
MenuButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = MenuPopup.IsMouseOver));
|
||||||
MenuPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(MenuPopup_PlacementCallback);
|
MenuPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
|
||||||
MenuPopup.Closed += (o, args) => MenuButton.Background = originalBrush;
|
MenuPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = MenuPopup.IsMouseOver));
|
||||||
MenuPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = IsMouseOver));
|
|
||||||
MenuPopup.Opened += (o, args) => MenuButton.Background = Brushes.LightGray;
|
|
||||||
KeyUp += BrowserWindow_KeyUp;
|
KeyUp += BrowserWindow_KeyUp;
|
||||||
|
LocationChanged += (o, args) => { DownloadsPopup.IsOpen = false; MenuPopup.IsOpen = false; };
|
||||||
ReloadButton.Click += (o, args) => ReloadRequested?.Invoke();
|
ReloadButton.Click += (o, args) => ReloadRequested?.Invoke();
|
||||||
|
SizeChanged += (o, args) => { DownloadsPopup.IsOpen = false; MenuPopup.IsOpen = false; };
|
||||||
SystemParameters.StaticPropertyChanged += SystemParameters_StaticPropertyChanged;
|
SystemParameters.StaticPropertyChanged += SystemParameters_StaticPropertyChanged;
|
||||||
UrlTextBox.GotKeyboardFocus += (o, args) => UrlTextBox.SelectAll();
|
UrlTextBox.GotKeyboardFocus += (o, args) => UrlTextBox.SelectAll();
|
||||||
UrlTextBox.GotMouseCapture += UrlTextBox_GotMouseCapture;
|
UrlTextBox.GotMouseCapture += UrlTextBox_GotMouseCapture;
|
||||||
|
@ -270,6 +303,7 @@ namespace SafeExamBrowser.UserInterface.Mobile
|
||||||
ForwardButton.Height = 35;
|
ForwardButton.Height = 35;
|
||||||
ReloadButton.Height = 35;
|
ReloadButton.Height = 35;
|
||||||
UrlTextBox.Height = 20;
|
UrlTextBox.Height = 20;
|
||||||
|
DownloadsButton.Height = 35;
|
||||||
MenuButton.Height = 35;
|
MenuButton.Height = 35;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.Browser.DownloadItemControl"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile.Controls.Browser"
|
||||||
|
mc:Ignorable="d" d:DesignHeight="50" d:DesignWidth="200">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<ContentControl Grid.Row="0" Grid.RowSpan="2" Grid.Column="0" Panel.ZIndex="2" Name="Icon" Margin="10" Width="25" />
|
||||||
|
<ProgressBar Grid.Row="0" Grid.RowSpan="2" Grid.Column="0" Grid.ColumnSpan="2" Panel.ZIndex="1" Name="Progress" BorderThickness="0" />
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="1" Panel.ZIndex="2" Name="ItemName" FontWeight="Bold" Margin="0,10,10,0" />
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="1" Panel.ZIndex="2" Name="Status" FontStyle="Italic" Margin="0,0,10,10" />
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using SafeExamBrowser.I18n.Contracts;
|
||||||
|
using SafeExamBrowser.UserInterface.Contracts.Browser.Data;
|
||||||
|
using SafeExamBrowser.UserInterface.Shared.Utilities;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.UserInterface.Mobile.Controls.Browser
|
||||||
|
{
|
||||||
|
public partial class DownloadItemControl : UserControl
|
||||||
|
{
|
||||||
|
private IText text;
|
||||||
|
|
||||||
|
public Guid Id { get; }
|
||||||
|
|
||||||
|
public DownloadItemControl(Guid id, IText text)
|
||||||
|
{
|
||||||
|
this.Id = id;
|
||||||
|
this.text = text;
|
||||||
|
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(DownloadItemState state)
|
||||||
|
{
|
||||||
|
ItemName.Text = Uri.TryCreate(state.Url, UriKind.Absolute, out var uri) ? Path.GetFileName(uri.AbsolutePath) : state.Url;
|
||||||
|
Progress.Value = state.Completion * 100;
|
||||||
|
Status.Text = $"{text.Get(TextKey.BrowserWindow_Downloading)} ({state.Completion * 100}%)";
|
||||||
|
|
||||||
|
if (File.Exists(state.FullPath))
|
||||||
|
{
|
||||||
|
ItemName.Text = Path.GetFileName(state.FullPath);
|
||||||
|
Icon.Content = new Image { Source = IconLoader.LoadIconFor(new FileInfo(state.FullPath)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.IsCancelled)
|
||||||
|
{
|
||||||
|
Progress.Visibility = System.Windows.Visibility.Collapsed;
|
||||||
|
Status.Text = text.Get(TextKey.BrowserWindow_DownloadCancelled);
|
||||||
|
}
|
||||||
|
else if (state.IsComplete)
|
||||||
|
{
|
||||||
|
Progress.Visibility = System.Windows.Visibility.Collapsed;
|
||||||
|
Status.Text = text.Get(TextKey.BrowserWindow_DownloadComplete);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,7 +22,7 @@ using SafeExamBrowser.UserInterface.Shared.Utilities;
|
||||||
|
|
||||||
namespace SafeExamBrowser.UserInterface.Mobile
|
namespace SafeExamBrowser.UserInterface.Mobile
|
||||||
{
|
{
|
||||||
public partial class FileSystemDialog : Window, IFileSystemDialog
|
public partial class FileSystemDialog : Window
|
||||||
{
|
{
|
||||||
private FileSystemElement element;
|
private FileSystemElement element;
|
||||||
private string initialPath;
|
private string initialPath;
|
||||||
|
@ -30,6 +30,7 @@ namespace SafeExamBrowser.UserInterface.Mobile
|
||||||
private FileSystemOperation operation;
|
private FileSystemOperation operation;
|
||||||
private IText text;
|
private IText text;
|
||||||
private string title;
|
private string title;
|
||||||
|
private IWindow parent;
|
||||||
|
|
||||||
public FileSystemDialog(
|
public FileSystemDialog(
|
||||||
FileSystemElement element,
|
FileSystemElement element,
|
||||||
|
@ -37,12 +38,14 @@ namespace SafeExamBrowser.UserInterface.Mobile
|
||||||
FileSystemOperation operation,
|
FileSystemOperation operation,
|
||||||
IText text,
|
IText text,
|
||||||
string message = default(string),
|
string message = default(string),
|
||||||
string title = default(string))
|
string title = default(string),
|
||||||
|
IWindow parent = default(IWindow))
|
||||||
{
|
{
|
||||||
this.element = element;
|
this.element = element;
|
||||||
this.initialPath = initialPath;
|
this.initialPath = initialPath;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.operation = operation;
|
this.operation = operation;
|
||||||
|
this.parent = parent;
|
||||||
this.text = text;
|
this.text = text;
|
||||||
this.title = title;
|
this.title = title;
|
||||||
|
|
||||||
|
@ -50,26 +53,23 @@ namespace SafeExamBrowser.UserInterface.Mobile
|
||||||
InitializeDialog();
|
InitializeDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileSystemDialogResult Show(IWindow parent = null)
|
internal new FileSystemDialogResult Show()
|
||||||
{
|
{
|
||||||
return Dispatcher.Invoke(() =>
|
var result = new FileSystemDialogResult();
|
||||||
|
|
||||||
|
if (parent is Window)
|
||||||
{
|
{
|
||||||
var result = new FileSystemDialogResult();
|
Owner = parent as Window;
|
||||||
|
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||||
|
}
|
||||||
|
|
||||||
if (parent is Window)
|
if (ShowDialog() == true)
|
||||||
{
|
{
|
||||||
Owner = parent as Window;
|
result.FullPath = BuildFullPath();
|
||||||
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
result.Success = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ShowDialog() == true)
|
return result;
|
||||||
{
|
|
||||||
result.FullPath = BuildFullPath();
|
|
||||||
result.Success = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CancelButton_Click(object sender, RoutedEventArgs e)
|
private void CancelButton_Click(object sender, RoutedEventArgs e)
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET)
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System.Windows;
|
||||||
|
using SafeExamBrowser.I18n.Contracts;
|
||||||
|
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
|
||||||
|
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.UserInterface.Mobile
|
||||||
|
{
|
||||||
|
public class FileSystemDialogFactory : IFileSystemDialog
|
||||||
|
{
|
||||||
|
private IText text;
|
||||||
|
|
||||||
|
public FileSystemDialogFactory(IText text)
|
||||||
|
{
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FileSystemDialogResult Show(FileSystemElement element, string initialPath, FileSystemOperation operation, string message = null, string title = null, IWindow owner = null)
|
||||||
|
{
|
||||||
|
if (owner is Window window)
|
||||||
|
{
|
||||||
|
return window.Dispatcher.Invoke(() => new FileSystemDialog(element, initialPath, operation, text, message, title, owner).Show());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new FileSystemDialog(element, initialPath, operation, text, message, title).Show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -110,6 +110,9 @@
|
||||||
<Compile Include="Controls\ActionCenterWirelessNetworkControl.xaml.cs">
|
<Compile Include="Controls\ActionCenterWirelessNetworkControl.xaml.cs">
|
||||||
<DependentUpon>ActionCenterWirelessNetworkControl.xaml</DependentUpon>
|
<DependentUpon>ActionCenterWirelessNetworkControl.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Controls\Browser\DownloadItemControl.xaml.cs">
|
||||||
|
<DependentUpon>DownloadItemControl.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Controls\TaskbarApplicationControl.xaml.cs">
|
<Compile Include="Controls\TaskbarApplicationControl.xaml.cs">
|
||||||
<DependentUpon>TaskbarApplicationControl.xaml</DependentUpon>
|
<DependentUpon>TaskbarApplicationControl.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
@ -149,6 +152,7 @@
|
||||||
<Compile Include="FileSystemDialog.xaml.cs">
|
<Compile Include="FileSystemDialog.xaml.cs">
|
||||||
<DependentUpon>FileSystemDialog.xaml</DependentUpon>
|
<DependentUpon>FileSystemDialog.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="FileSystemDialogFactory.cs" />
|
||||||
<Compile Include="FolderDialog.cs" />
|
<Compile Include="FolderDialog.cs" />
|
||||||
<Compile Include="LockScreen.xaml.cs">
|
<Compile Include="LockScreen.xaml.cs">
|
||||||
<DependentUpon>LockScreen.xaml</DependentUpon>
|
<DependentUpon>LockScreen.xaml</DependentUpon>
|
||||||
|
@ -279,6 +283,10 @@
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</Page>
|
</Page>
|
||||||
|
<Page Include="Controls\Browser\DownloadItemControl.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</Page>
|
||||||
<Page Include="Controls\TaskbarApplicationControl.xaml">
|
<Page Include="Controls\TaskbarApplicationControl.xaml">
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
|
|
|
@ -23,7 +23,6 @@ using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
|
||||||
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
|
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
|
||||||
using SafeExamBrowser.UserInterface.Contracts;
|
using SafeExamBrowser.UserInterface.Contracts;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Browser;
|
using SafeExamBrowser.UserInterface.Contracts.Browser;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
|
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
|
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
|
||||||
|
@ -76,11 +75,6 @@ namespace SafeExamBrowser.UserInterface.Mobile
|
||||||
return Application.Current.Dispatcher.Invoke(() => new BrowserWindow(control, settings, isMainWindow, text));
|
return Application.Current.Dispatcher.Invoke(() => new BrowserWindow(control, settings, isMainWindow, text));
|
||||||
}
|
}
|
||||||
|
|
||||||
public IFileSystemDialog CreateFileSystemDialog(FileSystemElement element, string initialPath, FileSystemOperation operation, string message = default(string), string title = default(string))
|
|
||||||
{
|
|
||||||
return Application.Current.Dispatcher.Invoke(() => new FileSystemDialog(element, initialPath, operation, text, message, title));
|
|
||||||
}
|
|
||||||
|
|
||||||
public IFolderDialog CreateFolderDialog(string message)
|
public IFolderDialog CreateFolderDialog(string message)
|
||||||
{
|
{
|
||||||
return new FolderDialog(message);
|
return new FolderDialog(message);
|
||||||
|
@ -105,26 +99,26 @@ namespace SafeExamBrowser.UserInterface.Mobile
|
||||||
|
|
||||||
public IWindow CreateLogWindow(ILogger logger)
|
public IWindow CreateLogWindow(ILogger logger)
|
||||||
{
|
{
|
||||||
LogWindow logWindow = null;
|
var window = default(LogWindow);
|
||||||
var logWindowReadyEvent = new AutoResetEvent(false);
|
var windowReadyEvent = new AutoResetEvent(false);
|
||||||
var logWindowThread = new Thread(() =>
|
var windowThread = new Thread(() =>
|
||||||
{
|
{
|
||||||
logWindow = new LogWindow(logger, text);
|
window = new LogWindow(logger, text);
|
||||||
logWindow.Closed += (o, args) => logWindow.Dispatcher.InvokeShutdown();
|
window.Closed += (o, args) => window.Dispatcher.InvokeShutdown();
|
||||||
logWindow.Show();
|
window.Show();
|
||||||
|
|
||||||
logWindowReadyEvent.Set();
|
windowReadyEvent.Set();
|
||||||
|
|
||||||
System.Windows.Threading.Dispatcher.Run();
|
System.Windows.Threading.Dispatcher.Run();
|
||||||
});
|
});
|
||||||
|
|
||||||
logWindowThread.SetApartmentState(ApartmentState.STA);
|
windowThread.SetApartmentState(ApartmentState.STA);
|
||||||
logWindowThread.IsBackground = true;
|
windowThread.IsBackground = true;
|
||||||
logWindowThread.Start();
|
windowThread.Start();
|
||||||
|
|
||||||
logWindowReadyEvent.WaitOne();
|
windowReadyEvent.WaitOne();
|
||||||
|
|
||||||
return logWindow;
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
public INotificationControl CreateNotificationControl(INotificationController controller, INotificationInfo info, Location location)
|
public INotificationControl CreateNotificationControl(INotificationController controller, INotificationInfo info, Location location)
|
||||||
|
@ -168,27 +162,26 @@ namespace SafeExamBrowser.UserInterface.Mobile
|
||||||
|
|
||||||
public ISplashScreen CreateSplashScreen(AppConfig appConfig = null)
|
public ISplashScreen CreateSplashScreen(AppConfig appConfig = null)
|
||||||
{
|
{
|
||||||
SplashScreen splashScreen = null;
|
var window = default(SplashScreen);
|
||||||
var splashReadyEvent = new AutoResetEvent(false);
|
var windowReadyEvent = new AutoResetEvent(false);
|
||||||
var splashScreenThread = new Thread(() =>
|
var windowThread = new Thread(() =>
|
||||||
{
|
{
|
||||||
splashScreen = new SplashScreen(text, appConfig);
|
window = new SplashScreen(text, appConfig);
|
||||||
splashScreen.Closed += (o, args) => splashScreen.Dispatcher.InvokeShutdown();
|
window.Closed += (o, args) => window.Dispatcher.InvokeShutdown();
|
||||||
splashScreen.Show();
|
window.Show();
|
||||||
|
|
||||||
splashReadyEvent.Set();
|
windowReadyEvent.Set();
|
||||||
|
|
||||||
System.Windows.Threading.Dispatcher.Run();
|
System.Windows.Threading.Dispatcher.Run();
|
||||||
});
|
});
|
||||||
|
|
||||||
splashScreenThread.SetApartmentState(ApartmentState.STA);
|
windowThread.SetApartmentState(ApartmentState.STA);
|
||||||
splashScreenThread.Name = nameof(SplashScreen);
|
windowThread.IsBackground = true;
|
||||||
splashScreenThread.IsBackground = true;
|
windowThread.Start();
|
||||||
splashScreenThread.Start();
|
|
||||||
|
|
||||||
splashReadyEvent.WaitOne();
|
windowReadyEvent.WaitOne();
|
||||||
|
|
||||||
return splashScreen;
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ISystemControl CreateWirelessNetworkControl(IWirelessAdapter wirelessAdapter, Location location)
|
public ISystemControl CreateWirelessNetworkControl(IWirelessAdapter wirelessAdapter, Location location)
|
||||||
|
|
Loading…
Reference in a new issue