SEBWIN-308: Implemented basic download overview for browser.

This commit is contained in:
dbuechel 2020-01-22 15:16:11 +01:00
parent b9536c6a1b
commit 97f3fb4a02
30 changed files with 582 additions and 133 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -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" />

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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" />

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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