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.Logging;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
|
||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
||||
using BrowserSettings = SafeExamBrowser.Settings.Browser.BrowserSettings;
|
||||
|
||||
|
@ -34,6 +35,7 @@ namespace SafeExamBrowser.Browser
|
|||
|
||||
private AppConfig appConfig;
|
||||
private List<BrowserApplicationInstance> instances;
|
||||
private IFileSystemDialog fileSystemDialog;
|
||||
private IMessageBox messageBox;
|
||||
private IModuleLogger logger;
|
||||
private BrowserSettings settings;
|
||||
|
@ -53,12 +55,14 @@ namespace SafeExamBrowser.Browser
|
|||
public BrowserApplication(
|
||||
AppConfig appConfig,
|
||||
BrowserSettings settings,
|
||||
IFileSystemDialog fileSystemDialog,
|
||||
IMessageBox messageBox,
|
||||
IModuleLogger logger,
|
||||
IText text,
|
||||
IUserInterfaceFactory uiFactory)
|
||||
{
|
||||
this.appConfig = appConfig;
|
||||
this.fileSystemDialog = fileSystemDialog;
|
||||
this.instances = new List<BrowserApplicationInstance>();
|
||||
this.logger = logger;
|
||||
this.messageBox = messageBox;
|
||||
|
@ -122,7 +126,7 @@ namespace SafeExamBrowser.Browser
|
|||
var isMainInstance = instances.Count == 0;
|
||||
var instanceLogger = logger.CloneFor($"Browser Instance #{id}");
|
||||
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.PopupRequested += Instance_PopupRequested;
|
||||
|
|
|
@ -24,6 +24,8 @@ using SafeExamBrowser.Settings.Browser;
|
|||
using SafeExamBrowser.Settings.Browser.Filter;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Browser;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Browser.Data;
|
||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
|
||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
||||
|
||||
namespace SafeExamBrowser.Browser
|
||||
|
@ -37,6 +39,7 @@ namespace SafeExamBrowser.Browser
|
|||
private IBrowserWindow window;
|
||||
private HttpClient httpClient;
|
||||
private bool isMainInstance;
|
||||
private IFileSystemDialog fileSystemDialog;
|
||||
private IMessageBox messageBox;
|
||||
private IModuleLogger logger;
|
||||
private BrowserSettings settings;
|
||||
|
@ -69,6 +72,7 @@ namespace SafeExamBrowser.Browser
|
|||
BrowserSettings settings,
|
||||
int id,
|
||||
bool isMainInstance,
|
||||
IFileSystemDialog fileSystemDialog,
|
||||
IMessageBox messageBox,
|
||||
IModuleLogger logger,
|
||||
IText text,
|
||||
|
@ -79,6 +83,7 @@ namespace SafeExamBrowser.Browser
|
|||
this.Id = id;
|
||||
this.httpClient = new HttpClient();
|
||||
this.isMainInstance = isMainInstance;
|
||||
this.fileSystemDialog = fileSystemDialog;
|
||||
this.messageBox = messageBox;
|
||||
this.logger = logger;
|
||||
this.settings = settings;
|
||||
|
@ -122,6 +127,7 @@ namespace SafeExamBrowser.Browser
|
|||
displayHandler.FaviconChanged += DisplayHandler_FaviconChanged;
|
||||
displayHandler.ProgressChanged += DisplayHandler_ProgressChanged;
|
||||
downloadHandler.ConfigurationDownloadRequested += DownloadHandler_ConfigurationDownloadRequested;
|
||||
downloadHandler.DownloadUpdated += DownloadHandler_DownloadUpdated;
|
||||
keyboardHandler.ReloadRequested += ReloadRequested;
|
||||
keyboardHandler.ZoomInRequested += ZoomInRequested;
|
||||
keyboardHandler.ZoomOutRequested += ZoomOutRequested;
|
||||
|
@ -211,8 +217,7 @@ namespace SafeExamBrowser.Browser
|
|||
|
||||
private void DialogHandler_DialogRequested(DialogRequestedEventArgs args)
|
||||
{
|
||||
var dialog = uiFactory.CreateFileSystemDialog(args.Element, args.InitialPath, args.Operation, title: args.Title);
|
||||
var result = dialog.Show(window);
|
||||
var result = fileSystemDialog.Show(args.Element, args.InitialPath, args.Operation, title: args.Title, owner: window);
|
||||
|
||||
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)
|
||||
{
|
||||
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 CefSharp;
|
||||
using SafeExamBrowser.Browser.Contracts.Events;
|
||||
using SafeExamBrowser.Browser.Events;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Browser.Data;
|
||||
using Syroot.Windows.IO;
|
||||
using BrowserSettings = SafeExamBrowser.Settings.Browser.BrowserSettings;
|
||||
|
||||
|
@ -24,14 +26,17 @@ namespace SafeExamBrowser.Browser.Handlers
|
|||
private AppConfig appConfig;
|
||||
private BrowserSettings settings;
|
||||
private ConcurrentDictionary<int, DownloadFinishedCallback> callbacks;
|
||||
private ConcurrentDictionary<int, Guid> downloads;
|
||||
private ILogger logger;
|
||||
|
||||
internal event DownloadRequestedEventHandler ConfigurationDownloadRequested;
|
||||
internal event DownloadUpdatedEventHandler DownloadUpdated;
|
||||
|
||||
internal DownloadHandler(AppConfig appConfig, BrowserSettings settings, ILogger logger)
|
||||
{
|
||||
this.appConfig = appConfig;
|
||||
this.callbacks = new ConcurrentDictionary<int, DownloadFinishedCallback>();
|
||||
this.downloads = new ConcurrentDictionary<int, Guid>();
|
||||
this.logger = logger;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
@ -60,18 +65,35 @@ namespace SafeExamBrowser.Browser.Handlers
|
|||
|
||||
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)
|
||||
{
|
||||
logger.Debug($"Download of '{downloadItem.Url}' {(downloadItem.IsComplete ? "is complete" : "was cancelled")}.");
|
||||
|
||||
if (callbacks.TryRemove(downloadItem.Id, out DownloadFinishedCallback finished) && finished != null)
|
||||
{
|
||||
Task.Run(() => finished.Invoke(downloadItem.IsComplete, downloadItem.FullPath));
|
||||
}
|
||||
|
||||
logger.Debug($"Download of '{downloadItem.Url}' {(downloadItem.IsComplete ? "is complete" : "was cancelled")}.");
|
||||
|
||||
// TODO: Show success message or download icon like Firefox in respective window!
|
||||
if (hasId)
|
||||
{
|
||||
downloads.TryRemove(downloadItem.Id, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,6 +122,8 @@ namespace SafeExamBrowser.Browser.Handlers
|
|||
logger.Debug($"Automatically downloading file as '{filePath}'.");
|
||||
}
|
||||
|
||||
downloads[downloadItem.Id] = Guid.NewGuid();
|
||||
|
||||
using (callback)
|
||||
{
|
||||
callback.Continue(filePath, showDialog);
|
||||
|
|
|
@ -68,6 +68,7 @@
|
|||
<Compile Include="BrowserApplicationInstance.cs" />
|
||||
<Compile Include="Events\DialogRequestedEventArgs.cs" />
|
||||
<Compile Include="Events\DialogRequestedEventHandler.cs" />
|
||||
<Compile Include="Events\DownloadUpdatedEventHandler.cs" />
|
||||
<Compile Include="Events\FaviconChangedEventHandler.cs" />
|
||||
<Compile Include="Events\InstanceTerminatedEventHandler.cs" />
|
||||
<Compile Include="Events\PopupRequestedEventArgs.cs" />
|
||||
|
|
|
@ -42,6 +42,7 @@ using SafeExamBrowser.SystemComponents.Keyboard;
|
|||
using SafeExamBrowser.SystemComponents.PowerSupply;
|
||||
using SafeExamBrowser.SystemComponents.WirelessNetwork;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
|
||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||
using SafeExamBrowser.UserInterface.Shared.Activators;
|
||||
|
@ -197,8 +198,9 @@ namespace SafeExamBrowser.Client
|
|||
|
||||
private IOperation BuildBrowserOperation()
|
||||
{
|
||||
var fileSystemDialog = BuildFileSystemDialog();
|
||||
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);
|
||||
|
||||
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()
|
||||
{
|
||||
switch (uiMode)
|
||||
|
|
|
@ -22,6 +22,9 @@ namespace SafeExamBrowser.I18n.Contracts
|
|||
Browser_Name,
|
||||
Browser_Tooltip,
|
||||
BrowserWindow_DeveloperConsoleMenuItem,
|
||||
BrowserWindow_Downloading,
|
||||
BrowserWindow_DownloadCancelled,
|
||||
BrowserWindow_DownloadComplete,
|
||||
BrowserWindow_ZoomMenuItem,
|
||||
Build,
|
||||
FileSystemDialog_Cancel,
|
||||
|
|
|
@ -24,6 +24,15 @@
|
|||
<Entry key="BrowserWindow_DeveloperConsoleMenuItem">
|
||||
Developer Console
|
||||
</Entry>
|
||||
<Entry key="BrowserWindow_Downloading">
|
||||
Downloading...
|
||||
</Entry>
|
||||
<Entry key="BrowserWindow_DownloadCancelled">
|
||||
Cancelled.
|
||||
</Entry>
|
||||
<Entry key="BrowserWindow_DownloadComplete">
|
||||
Downloaded.
|
||||
</Entry>
|
||||
<Entry key="BrowserWindow_ZoomMenuItem">
|
||||
Page Zoom
|
||||
</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 SafeExamBrowser.Applications.Contracts.Resources.Icons;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Browser.Data;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Browser.Events;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||
|
||||
|
@ -83,6 +84,11 @@ namespace SafeExamBrowser.UserInterface.Contracts.Browser
|
|||
/// </summary>
|
||||
void UpdateIcon(IconResource icon);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the download state for the given item.
|
||||
/// </summary>
|
||||
void UpdateDownloadState(DownloadItemState state);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the loading state according to the given value.
|
||||
/// </summary>
|
||||
|
|
|
@ -13,8 +13,8 @@ namespace SafeExamBrowser.UserInterface.Contracts.FileSystemDialog
|
|||
public interface IFileSystemDialog
|
||||
{
|
||||
/// <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>
|
||||
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.WirelessNetwork;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Browser;
|
||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
|
||||
|
@ -50,11 +49,6 @@ namespace SafeExamBrowser.UserInterface.Contracts
|
|||
/// </summary>
|
||||
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>
|
||||
/// Creates a folder dialog with the given message.
|
||||
/// </summary>
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
<Reference Include="Microsoft.CSharp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Browser\Data\DownloadItemState.cs" />
|
||||
<Compile Include="Browser\Events\AddressChangedEventHandler.cs" />
|
||||
<Compile Include="Browser\Events\LoadingStateChangedEventHandler.cs" />
|
||||
<Compile Include="Browser\Events\TitleChangedEventHandler.cs" />
|
||||
|
|
|
@ -33,12 +33,21 @@
|
|||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<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="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" />
|
||||
<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}">
|
||||
<Border Background="{StaticResource BackgroundBrush}" BorderBrush="LightGray" BorderThickness="1,0,1,1" Width="250">
|
||||
<StackPanel Orientation="Vertical">
|
||||
|
|
|
@ -13,16 +13,17 @@ using System.Windows;
|
|||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using SafeExamBrowser.Applications.Contracts.Resources.Icons;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Settings.Browser;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Browser;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Browser.Data;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Browser.Events;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Events;
|
||||
using SafeExamBrowser.UserInterface.Desktop.Controls.Browser;
|
||||
using SafeExamBrowser.UserInterface.Shared.Utilities;
|
||||
|
||||
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)
|
||||
{
|
||||
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[]
|
||||
{
|
||||
|
@ -216,21 +247,23 @@ namespace SafeExamBrowser.UserInterface.Desktop
|
|||
|
||||
private void RegisterEvents()
|
||||
{
|
||||
var originalBrush = MenuButton.Background;
|
||||
|
||||
BackwardButton.Click += (o, args) => BackwardNavigationRequested?.Invoke();
|
||||
Closing += BrowserWindow_Closing;
|
||||
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();
|
||||
Loaded += BrowserWindow_Loaded;
|
||||
MenuButton.Click += (o, args) => MenuPopup.IsOpen = !MenuPopup.IsOpen;
|
||||
MenuButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = MenuPopup.IsMouseOver));
|
||||
MenuPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(MenuPopup_PlacementCallback);
|
||||
MenuPopup.Closed += (o, args) => MenuButton.Background = originalBrush;
|
||||
MenuPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = IsMouseOver));
|
||||
MenuPopup.Opened += (o, args) => MenuButton.Background = Brushes.LightGray;
|
||||
MenuPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
|
||||
MenuPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = MenuPopup.IsMouseOver));
|
||||
KeyUp += BrowserWindow_KeyUp;
|
||||
LocationChanged += (o, args) => { DownloadsPopup.IsOpen = false; MenuPopup.IsOpen = false; };
|
||||
ReloadButton.Click += (o, args) => ReloadRequested?.Invoke();
|
||||
SizeChanged += (o, args) => { DownloadsPopup.IsOpen = false; MenuPopup.IsOpen = false; };
|
||||
SystemParameters.StaticPropertyChanged += SystemParameters_StaticPropertyChanged;
|
||||
UrlTextBox.GotKeyboardFocus += (o, args) => UrlTextBox.SelectAll();
|
||||
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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
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>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
|
|
|
@ -22,12 +22,13 @@ using SafeExamBrowser.UserInterface.Shared.Utilities;
|
|||
|
||||
namespace SafeExamBrowser.UserInterface.Desktop
|
||||
{
|
||||
public partial class FileSystemDialog : Window, IFileSystemDialog
|
||||
public partial class FileSystemDialog : Window
|
||||
{
|
||||
private FileSystemElement element;
|
||||
private string initialPath;
|
||||
private string message;
|
||||
private FileSystemOperation operation;
|
||||
private IWindow parent;
|
||||
private IText text;
|
||||
private string title;
|
||||
|
||||
|
@ -37,12 +38,14 @@ namespace SafeExamBrowser.UserInterface.Desktop
|
|||
FileSystemOperation operation,
|
||||
IText text,
|
||||
string message = default(string),
|
||||
string title = default(string))
|
||||
string title = default(string),
|
||||
IWindow parent = default(IWindow))
|
||||
{
|
||||
this.element = element;
|
||||
this.initialPath = initialPath;
|
||||
this.message = message;
|
||||
this.operation = operation;
|
||||
this.parent = parent;
|
||||
this.text = text;
|
||||
this.title = title;
|
||||
|
||||
|
@ -50,26 +53,23 @@ namespace SafeExamBrowser.UserInterface.Desktop
|
|||
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)
|
||||
{
|
||||
Owner = parent as Window;
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||
}
|
||||
if (ShowDialog() == true)
|
||||
{
|
||||
result.FullPath = BuildFullPath();
|
||||
result.Success = true;
|
||||
}
|
||||
|
||||
if (ShowDialog() == true)
|
||||
{
|
||||
result.FullPath = BuildFullPath();
|
||||
result.Success = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
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">
|
||||
<DependentUpon>ActionCenterWirelessNetworkControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Controls\Browser\DownloadItemControl.xaml.cs">
|
||||
<DependentUpon>DownloadItemControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Controls\TaskbarApplicationControl.xaml.cs">
|
||||
<DependentUpon>TaskbarApplicationControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -148,6 +151,7 @@
|
|||
<Compile Include="FileSystemDialog.xaml.cs">
|
||||
<DependentUpon>FileSystemDialog.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="FileSystemDialogFactory.cs" />
|
||||
<Compile Include="FolderDialog.cs" />
|
||||
<Compile Include="LockScreen.xaml.cs">
|
||||
<DependentUpon>LockScreen.xaml</DependentUpon>
|
||||
|
@ -229,6 +233,10 @@
|
|||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Controls\Browser\DownloadItemControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="Controls\TaskbarApplicationControl.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
|
|
@ -23,7 +23,6 @@ using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
|
|||
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Browser;
|
||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||
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));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return new FolderDialog(message);
|
||||
|
@ -105,26 +99,26 @@ namespace SafeExamBrowser.UserInterface.Desktop
|
|||
|
||||
public IWindow CreateLogWindow(ILogger logger)
|
||||
{
|
||||
LogWindow logWindow = null;
|
||||
var logWindowReadyEvent = new AutoResetEvent(false);
|
||||
var logWindowThread = new Thread(() =>
|
||||
var window = default(LogWindow);
|
||||
var windowReadyEvent = new AutoResetEvent(false);
|
||||
var windowThread = new Thread(() =>
|
||||
{
|
||||
logWindow = new LogWindow(logger, text);
|
||||
logWindow.Closed += (o, args) => logWindow.Dispatcher.InvokeShutdown();
|
||||
logWindow.Show();
|
||||
window = new LogWindow(logger, text);
|
||||
window.Closed += (o, args) => window.Dispatcher.InvokeShutdown();
|
||||
window.Show();
|
||||
|
||||
logWindowReadyEvent.Set();
|
||||
windowReadyEvent.Set();
|
||||
|
||||
System.Windows.Threading.Dispatcher.Run();
|
||||
});
|
||||
|
||||
logWindowThread.SetApartmentState(ApartmentState.STA);
|
||||
logWindowThread.IsBackground = true;
|
||||
logWindowThread.Start();
|
||||
windowThread.SetApartmentState(ApartmentState.STA);
|
||||
windowThread.IsBackground = true;
|
||||
windowThread.Start();
|
||||
|
||||
logWindowReadyEvent.WaitOne();
|
||||
windowReadyEvent.WaitOne();
|
||||
|
||||
return logWindow;
|
||||
return window;
|
||||
}
|
||||
|
||||
public INotificationControl CreateNotificationControl(INotificationController controller, INotificationInfo info, Location location)
|
||||
|
@ -168,27 +162,26 @@ namespace SafeExamBrowser.UserInterface.Desktop
|
|||
|
||||
public ISplashScreen CreateSplashScreen(AppConfig appConfig = null)
|
||||
{
|
||||
SplashScreen splashScreen = null;
|
||||
var splashReadyEvent = new AutoResetEvent(false);
|
||||
var splashScreenThread = new Thread(() =>
|
||||
var window = default(SplashScreen);
|
||||
var windowReadyEvent = new AutoResetEvent(false);
|
||||
var windowThread = new Thread(() =>
|
||||
{
|
||||
splashScreen = new SplashScreen(text, appConfig);
|
||||
splashScreen.Closed += (o, args) => splashScreen.Dispatcher.InvokeShutdown();
|
||||
splashScreen.Show();
|
||||
window = new SplashScreen(text, appConfig);
|
||||
window.Closed += (o, args) => window.Dispatcher.InvokeShutdown();
|
||||
window.Show();
|
||||
|
||||
splashReadyEvent.Set();
|
||||
windowReadyEvent.Set();
|
||||
|
||||
System.Windows.Threading.Dispatcher.Run();
|
||||
});
|
||||
|
||||
splashScreenThread.SetApartmentState(ApartmentState.STA);
|
||||
splashScreenThread.Name = nameof(SplashScreen);
|
||||
splashScreenThread.IsBackground = true;
|
||||
splashScreenThread.Start();
|
||||
windowThread.SetApartmentState(ApartmentState.STA);
|
||||
windowThread.IsBackground = true;
|
||||
windowThread.Start();
|
||||
|
||||
splashReadyEvent.WaitOne();
|
||||
windowReadyEvent.WaitOne();
|
||||
|
||||
return splashScreen;
|
||||
return window;
|
||||
}
|
||||
|
||||
public ISystemControl CreateWirelessNetworkControl(IWirelessAdapter wirelessAdapter, Location location)
|
||||
|
|
|
@ -33,12 +33,21 @@
|
|||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<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="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" />
|
||||
<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}">
|
||||
<Border Background="{StaticResource BackgroundBrush}" BorderBrush="LightGray" BorderThickness="1,0,1,1" Width="350">
|
||||
<StackPanel Orientation="Vertical">
|
||||
|
|
|
@ -13,16 +13,17 @@ using System.Windows;
|
|||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using SafeExamBrowser.Applications.Contracts.Resources.Icons;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Settings.Browser;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Browser;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Browser.Data;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Browser.Events;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Events;
|
||||
using SafeExamBrowser.UserInterface.Mobile.Controls.Browser;
|
||||
using SafeExamBrowser.UserInterface.Shared.Utilities;
|
||||
|
||||
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)
|
||||
{
|
||||
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[]
|
||||
{
|
||||
|
@ -216,21 +247,23 @@ namespace SafeExamBrowser.UserInterface.Mobile
|
|||
|
||||
private void RegisterEvents()
|
||||
{
|
||||
var originalBrush = MenuButton.Background;
|
||||
|
||||
BackwardButton.Click += (o, args) => BackwardNavigationRequested?.Invoke();
|
||||
Closing += BrowserWindow_Closing;
|
||||
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();
|
||||
Loaded += BrowserWindow_Loaded;
|
||||
MenuButton.Click += (o, args) => MenuPopup.IsOpen = !MenuPopup.IsOpen;
|
||||
MenuButton.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = MenuPopup.IsMouseOver));
|
||||
MenuPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(MenuPopup_PlacementCallback);
|
||||
MenuPopup.Closed += (o, args) => MenuButton.Background = originalBrush;
|
||||
MenuPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = IsMouseOver));
|
||||
MenuPopup.Opened += (o, args) => MenuButton.Background = Brushes.LightGray;
|
||||
MenuPopup.CustomPopupPlacementCallback = new CustomPopupPlacementCallback(Popup_PlacementCallback);
|
||||
MenuPopup.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => MenuPopup.IsOpen = MenuPopup.IsMouseOver));
|
||||
KeyUp += BrowserWindow_KeyUp;
|
||||
LocationChanged += (o, args) => { DownloadsPopup.IsOpen = false; MenuPopup.IsOpen = false; };
|
||||
ReloadButton.Click += (o, args) => ReloadRequested?.Invoke();
|
||||
SizeChanged += (o, args) => { DownloadsPopup.IsOpen = false; MenuPopup.IsOpen = false; };
|
||||
SystemParameters.StaticPropertyChanged += SystemParameters_StaticPropertyChanged;
|
||||
UrlTextBox.GotKeyboardFocus += (o, args) => UrlTextBox.SelectAll();
|
||||
UrlTextBox.GotMouseCapture += UrlTextBox_GotMouseCapture;
|
||||
|
@ -270,6 +303,7 @@ namespace SafeExamBrowser.UserInterface.Mobile
|
|||
ForwardButton.Height = 35;
|
||||
ReloadButton.Height = 35;
|
||||
UrlTextBox.Height = 20;
|
||||
DownloadsButton.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
|
||||
{
|
||||
public partial class FileSystemDialog : Window, IFileSystemDialog
|
||||
public partial class FileSystemDialog : Window
|
||||
{
|
||||
private FileSystemElement element;
|
||||
private string initialPath;
|
||||
|
@ -30,6 +30,7 @@ namespace SafeExamBrowser.UserInterface.Mobile
|
|||
private FileSystemOperation operation;
|
||||
private IText text;
|
||||
private string title;
|
||||
private IWindow parent;
|
||||
|
||||
public FileSystemDialog(
|
||||
FileSystemElement element,
|
||||
|
@ -37,12 +38,14 @@ namespace SafeExamBrowser.UserInterface.Mobile
|
|||
FileSystemOperation operation,
|
||||
IText text,
|
||||
string message = default(string),
|
||||
string title = default(string))
|
||||
string title = default(string),
|
||||
IWindow parent = default(IWindow))
|
||||
{
|
||||
this.element = element;
|
||||
this.initialPath = initialPath;
|
||||
this.message = message;
|
||||
this.operation = operation;
|
||||
this.parent = parent;
|
||||
this.text = text;
|
||||
this.title = title;
|
||||
|
||||
|
@ -50,26 +53,23 @@ namespace SafeExamBrowser.UserInterface.Mobile
|
|||
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)
|
||||
{
|
||||
Owner = parent as Window;
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||
}
|
||||
if (ShowDialog() == true)
|
||||
{
|
||||
result.FullPath = BuildFullPath();
|
||||
result.Success = true;
|
||||
}
|
||||
|
||||
if (ShowDialog() == true)
|
||||
{
|
||||
result.FullPath = BuildFullPath();
|
||||
result.Success = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
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">
|
||||
<DependentUpon>ActionCenterWirelessNetworkControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Controls\Browser\DownloadItemControl.xaml.cs">
|
||||
<DependentUpon>DownloadItemControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Controls\TaskbarApplicationControl.xaml.cs">
|
||||
<DependentUpon>TaskbarApplicationControl.xaml</DependentUpon>
|
||||
</Compile>
|
||||
|
@ -149,6 +152,7 @@
|
|||
<Compile Include="FileSystemDialog.xaml.cs">
|
||||
<DependentUpon>FileSystemDialog.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="FileSystemDialogFactory.cs" />
|
||||
<Compile Include="FolderDialog.cs" />
|
||||
<Compile Include="LockScreen.xaml.cs">
|
||||
<DependentUpon>LockScreen.xaml</DependentUpon>
|
||||
|
@ -279,6 +283,10 @@
|
|||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Controls\Browser\DownloadItemControl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="Controls\TaskbarApplicationControl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
|
|
|
@ -23,7 +23,6 @@ using SafeExamBrowser.SystemComponents.Contracts.PowerSupply;
|
|||
using SafeExamBrowser.SystemComponents.Contracts.WirelessNetwork;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Browser;
|
||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||
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));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return new FolderDialog(message);
|
||||
|
@ -105,26 +99,26 @@ namespace SafeExamBrowser.UserInterface.Mobile
|
|||
|
||||
public IWindow CreateLogWindow(ILogger logger)
|
||||
{
|
||||
LogWindow logWindow = null;
|
||||
var logWindowReadyEvent = new AutoResetEvent(false);
|
||||
var logWindowThread = new Thread(() =>
|
||||
var window = default(LogWindow);
|
||||
var windowReadyEvent = new AutoResetEvent(false);
|
||||
var windowThread = new Thread(() =>
|
||||
{
|
||||
logWindow = new LogWindow(logger, text);
|
||||
logWindow.Closed += (o, args) => logWindow.Dispatcher.InvokeShutdown();
|
||||
logWindow.Show();
|
||||
window = new LogWindow(logger, text);
|
||||
window.Closed += (o, args) => window.Dispatcher.InvokeShutdown();
|
||||
window.Show();
|
||||
|
||||
logWindowReadyEvent.Set();
|
||||
windowReadyEvent.Set();
|
||||
|
||||
System.Windows.Threading.Dispatcher.Run();
|
||||
});
|
||||
|
||||
logWindowThread.SetApartmentState(ApartmentState.STA);
|
||||
logWindowThread.IsBackground = true;
|
||||
logWindowThread.Start();
|
||||
windowThread.SetApartmentState(ApartmentState.STA);
|
||||
windowThread.IsBackground = true;
|
||||
windowThread.Start();
|
||||
|
||||
logWindowReadyEvent.WaitOne();
|
||||
windowReadyEvent.WaitOne();
|
||||
|
||||
return logWindow;
|
||||
return window;
|
||||
}
|
||||
|
||||
public INotificationControl CreateNotificationControl(INotificationController controller, INotificationInfo info, Location location)
|
||||
|
@ -168,27 +162,26 @@ namespace SafeExamBrowser.UserInterface.Mobile
|
|||
|
||||
public ISplashScreen CreateSplashScreen(AppConfig appConfig = null)
|
||||
{
|
||||
SplashScreen splashScreen = null;
|
||||
var splashReadyEvent = new AutoResetEvent(false);
|
||||
var splashScreenThread = new Thread(() =>
|
||||
var window = default(SplashScreen);
|
||||
var windowReadyEvent = new AutoResetEvent(false);
|
||||
var windowThread = new Thread(() =>
|
||||
{
|
||||
splashScreen = new SplashScreen(text, appConfig);
|
||||
splashScreen.Closed += (o, args) => splashScreen.Dispatcher.InvokeShutdown();
|
||||
splashScreen.Show();
|
||||
window = new SplashScreen(text, appConfig);
|
||||
window.Closed += (o, args) => window.Dispatcher.InvokeShutdown();
|
||||
window.Show();
|
||||
|
||||
splashReadyEvent.Set();
|
||||
windowReadyEvent.Set();
|
||||
|
||||
System.Windows.Threading.Dispatcher.Run();
|
||||
});
|
||||
|
||||
splashScreenThread.SetApartmentState(ApartmentState.STA);
|
||||
splashScreenThread.Name = nameof(SplashScreen);
|
||||
splashScreenThread.IsBackground = true;
|
||||
splashScreenThread.Start();
|
||||
windowThread.SetApartmentState(ApartmentState.STA);
|
||||
windowThread.IsBackground = true;
|
||||
windowThread.Start();
|
||||
|
||||
splashReadyEvent.WaitOne();
|
||||
windowReadyEvent.WaitOne();
|
||||
|
||||
return splashScreen;
|
||||
return window;
|
||||
}
|
||||
|
||||
public ISystemControl CreateWirelessNetworkControl(IWirelessAdapter wirelessAdapter, Location location)
|
||||
|
|
Loading…
Reference in a new issue