SEBWIN-312: Started implementing task view.
This commit is contained in:
parent
5f31656649
commit
08bf49b61b
19 changed files with 248 additions and 33 deletions
|
@ -28,6 +28,6 @@ namespace SafeExamBrowser.Applications.Contracts
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The resource providing the application icon.
|
/// The resource providing the application icon.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IconResource IconResource { get; set; }
|
public IconResource Icon { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using SafeExamBrowser.Applications.Contracts.Events;
|
using SafeExamBrowser.Applications.Contracts.Events;
|
||||||
|
using SafeExamBrowser.Core.Contracts;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Applications.Contracts
|
namespace SafeExamBrowser.Applications.Contracts
|
||||||
{
|
{
|
||||||
|
@ -15,6 +16,11 @@ namespace SafeExamBrowser.Applications.Contracts
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IApplicationInstance
|
public interface IApplicationInstance
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The icon resource for this instance.
|
||||||
|
/// </summary>
|
||||||
|
IconResource Icon { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The unique identifier for the application instance.
|
/// The unique identifier for the application instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -64,7 +64,7 @@ namespace SafeExamBrowser.Applications
|
||||||
private IApplication BuildApplication(string executablePath, WhitelistApplication settings)
|
private IApplication BuildApplication(string executablePath, WhitelistApplication settings)
|
||||||
{
|
{
|
||||||
var icon = new IconResource { Type = IconResourceType.Embedded, Uri = new Uri(executablePath) };
|
var icon = new IconResource { Type = IconResourceType.Embedded, Uri = new Uri(executablePath) };
|
||||||
var info = new ApplicationInfo { IconResource = icon, Name = settings.DisplayName, Tooltip = settings.Description ?? settings.DisplayName };
|
var info = new ApplicationInfo { Icon = icon, Name = settings.DisplayName, Tooltip = settings.Description ?? settings.DisplayName };
|
||||||
var application = new ExternalApplication(executablePath, info, logger.CloneFor(settings.DisplayName), processFactory);
|
var application = new ExternalApplication(executablePath, info, logger.CloneFor(settings.DisplayName), processFactory);
|
||||||
|
|
||||||
return application;
|
return application;
|
||||||
|
|
|
@ -44,9 +44,11 @@ namespace SafeExamBrowser.Applications
|
||||||
{
|
{
|
||||||
logger.Info("Starting application...");
|
logger.Info("Starting application...");
|
||||||
|
|
||||||
|
// TODO: Ensure that SEB does not crash if an application cannot be started!!
|
||||||
|
|
||||||
var process = processFactory.StartNew(executablePath);
|
var process = processFactory.StartNew(executablePath);
|
||||||
var id = new ApplicationInstanceIdentifier(process.Id);
|
var id = new ApplicationInstanceIdentifier(process.Id);
|
||||||
var instance = new ExternalApplicationInstance(id, logger.CloneFor($"{Info.Name} {id}"), process);
|
var instance = new ExternalApplicationInstance(Info.Icon, id, logger.CloneFor($"{Info.Name} {id}"), process);
|
||||||
|
|
||||||
instance.Initialize();
|
instance.Initialize();
|
||||||
instances.Add(instance);
|
instances.Add(instance);
|
||||||
|
|
|
@ -10,6 +10,7 @@ using System;
|
||||||
using System.Timers;
|
using System.Timers;
|
||||||
using SafeExamBrowser.Applications.Contracts;
|
using SafeExamBrowser.Applications.Contracts;
|
||||||
using SafeExamBrowser.Applications.Contracts.Events;
|
using SafeExamBrowser.Applications.Contracts.Events;
|
||||||
|
using SafeExamBrowser.Core.Contracts;
|
||||||
using SafeExamBrowser.Logging.Contracts;
|
using SafeExamBrowser.Logging.Contracts;
|
||||||
using SafeExamBrowser.WindowsApi.Contracts;
|
using SafeExamBrowser.WindowsApi.Contracts;
|
||||||
|
|
||||||
|
@ -24,6 +25,7 @@ namespace SafeExamBrowser.Applications
|
||||||
private IProcess process;
|
private IProcess process;
|
||||||
private Timer timer;
|
private Timer timer;
|
||||||
|
|
||||||
|
public IconResource Icon { get; }
|
||||||
public InstanceIdentifier Id { get; }
|
public InstanceIdentifier Id { get; }
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
|
|
||||||
|
@ -31,8 +33,9 @@ namespace SafeExamBrowser.Applications
|
||||||
public event NameChangedEventHandler NameChanged;
|
public event NameChangedEventHandler NameChanged;
|
||||||
public event InstanceTerminatedEventHandler Terminated;
|
public event InstanceTerminatedEventHandler Terminated;
|
||||||
|
|
||||||
public ExternalApplicationInstance(InstanceIdentifier id, ILogger logger, IProcess process)
|
public ExternalApplicationInstance(IconResource icon, InstanceIdentifier id, ILogger logger, IProcess process)
|
||||||
{
|
{
|
||||||
|
this.Icon = icon;
|
||||||
this.Id = id;
|
this.Id = id;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.process = process;
|
this.process = process;
|
||||||
|
|
|
@ -65,7 +65,7 @@ namespace SafeExamBrowser.Browser
|
||||||
var cefSettings = InitializeCefSettings();
|
var cefSettings = InitializeCefSettings();
|
||||||
var success = Cef.Initialize(cefSettings, true, default(IApp));
|
var success = Cef.Initialize(cefSettings, true, default(IApp));
|
||||||
|
|
||||||
Info = BuildApplicationInfo();
|
InitializeApplicationInfo();
|
||||||
|
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
|
@ -95,11 +95,11 @@ namespace SafeExamBrowser.Browser
|
||||||
logger.Info("Terminated browser.");
|
logger.Info("Terminated browser.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private ApplicationInfo BuildApplicationInfo()
|
private void InitializeApplicationInfo()
|
||||||
{
|
{
|
||||||
return new ApplicationInfo
|
Info = new ApplicationInfo
|
||||||
{
|
{
|
||||||
IconResource = new BrowserIconResource(),
|
Icon = new BrowserIconResource(),
|
||||||
Name = "Safe Exam Browser",
|
Name = "Safe Exam Browser",
|
||||||
Tooltip = text.Get(TextKey.Browser_Tooltip)
|
Tooltip = text.Get(TextKey.Browser_Tooltip)
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,6 +17,7 @@ using SafeExamBrowser.Browser.Events;
|
||||||
using SafeExamBrowser.Browser.Filters;
|
using SafeExamBrowser.Browser.Filters;
|
||||||
using SafeExamBrowser.Browser.Handlers;
|
using SafeExamBrowser.Browser.Handlers;
|
||||||
using SafeExamBrowser.Configuration.Contracts;
|
using SafeExamBrowser.Configuration.Contracts;
|
||||||
|
using SafeExamBrowser.Core.Contracts;
|
||||||
using SafeExamBrowser.I18n.Contracts;
|
using SafeExamBrowser.I18n.Contracts;
|
||||||
using SafeExamBrowser.Logging.Contracts;
|
using SafeExamBrowser.Logging.Contracts;
|
||||||
using SafeExamBrowser.Settings.Browser;
|
using SafeExamBrowser.Settings.Browser;
|
||||||
|
@ -48,6 +49,7 @@ namespace SafeExamBrowser.Browser
|
||||||
get { return isMainInstance ? settings.MainWindow : settings.AdditionalWindow; }
|
get { return isMainInstance ? settings.MainWindow : settings.AdditionalWindow; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IconResource Icon { get; private set; }
|
||||||
public InstanceIdentifier Id { get; private set; }
|
public InstanceIdentifier Id { get; private set; }
|
||||||
public string Name { get; private set; }
|
public string Name { get; private set; }
|
||||||
|
|
||||||
|
@ -108,6 +110,8 @@ namespace SafeExamBrowser.Browser
|
||||||
var requestLogger = logger.CloneFor($"{nameof(RequestHandler)} {Id}");
|
var requestLogger = logger.CloneFor($"{nameof(RequestHandler)} {Id}");
|
||||||
var requestHandler = new RequestHandler(appConfig, settings.Filter, requestFilter, requestLogger, text);
|
var requestHandler = new RequestHandler(appConfig, settings.Filter, requestFilter, requestLogger, text);
|
||||||
|
|
||||||
|
Icon = new BrowserIconResource();
|
||||||
|
|
||||||
displayHandler.FaviconChanged += DisplayHandler_FaviconChanged;
|
displayHandler.FaviconChanged += DisplayHandler_FaviconChanged;
|
||||||
displayHandler.ProgressChanged += DisplayHandler_ProgressChanged;
|
displayHandler.ProgressChanged += DisplayHandler_ProgressChanged;
|
||||||
downloadHandler.ConfigurationDownloadRequested += DownloadHandler_ConfigurationDownloadRequested;
|
downloadHandler.ConfigurationDownloadRequested += DownloadHandler_ConfigurationDownloadRequested;
|
||||||
|
@ -201,10 +205,10 @@ namespace SafeExamBrowser.Browser
|
||||||
{
|
{
|
||||||
if (task.IsCompleted && task.Result.IsSuccessStatusCode)
|
if (task.IsCompleted && task.Result.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
var icon = new BrowserIconResource(uri);
|
Icon = new BrowserIconResource(uri);
|
||||||
|
|
||||||
IconChanged?.Invoke(icon);
|
IconChanged?.Invoke(Icon);
|
||||||
window.UpdateIcon(icon);
|
window.UpdateIcon(Icon);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,14 +98,12 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
||||||
var terminationActivator = new Mock<ITerminationActivator>();
|
var terminationActivator = new Mock<ITerminationActivator>();
|
||||||
|
|
||||||
context.Activators.Add(actionCenterActivator.Object);
|
context.Activators.Add(actionCenterActivator.Object);
|
||||||
context.Activators.Add(taskViewActivator.Object);
|
|
||||||
context.Activators.Add(terminationActivator.Object);
|
context.Activators.Add(terminationActivator.Object);
|
||||||
context.Settings.ActionCenter.EnableActionCenter = true;
|
context.Settings.ActionCenter.EnableActionCenter = true;
|
||||||
|
|
||||||
sut.Perform();
|
sut.Perform();
|
||||||
|
|
||||||
actionCenterActivator.Verify(a => a.Start(), Times.Once);
|
actionCenterActivator.Verify(a => a.Start(), Times.Once);
|
||||||
taskViewActivator.Verify(a => a.Start(), Times.Once);
|
|
||||||
terminationActivator.Verify(a => a.Start(), Times.Once);
|
terminationActivator.Verify(a => a.Start(), Times.Once);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,6 +174,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void Perform_MustInitializeTaskView()
|
public void Perform_MustInitializeTaskView()
|
||||||
{
|
{
|
||||||
|
// Only start activator if ALT+TAB enabled!
|
||||||
Assert.Fail("TODO");
|
Assert.Fail("TODO");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -113,7 +113,7 @@ namespace SafeExamBrowser.Client.Operations
|
||||||
actionCenterActivator.Start();
|
actionCenterActivator.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activator is ITaskViewActivator taskViewActivator)
|
if (Context.Settings.Keyboard.AllowAltTab && activator is ITaskViewActivator taskViewActivator)
|
||||||
{
|
{
|
||||||
taskView.Register(taskViewActivator);
|
taskView.Register(taskViewActivator);
|
||||||
taskViewActivator.Start();
|
taskViewActivator.Start();
|
||||||
|
|
|
@ -16,13 +16,18 @@ namespace SafeExamBrowser.UserInterface.Contracts.Shell
|
||||||
public interface ITaskViewActivator : IActivator
|
public interface ITaskViewActivator : IActivator
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fired when the next application instance should be selected.
|
/// Fired when the task view should be hidden.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event ActivatorEventHandler Next;
|
event ActivatorEventHandler Deactivated;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fired when the previous application instance should be selected.
|
/// Fired when the task view should be made visible and the next application instance should be selected.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event ActivatorEventHandler Previous;
|
event ActivatorEventHandler NextActivated;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fired when the task view should be made visible and the previous application instance should be selected.
|
||||||
|
/// </summary>
|
||||||
|
event ActivatorEventHandler PreviousActivated;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
|
||||||
|
|
||||||
private void InitializeApplicationInstanceButton()
|
private void InitializeApplicationInstanceButton()
|
||||||
{
|
{
|
||||||
Icon.Content = IconResourceLoader.Load(info.IconResource);
|
Icon.Content = IconResourceLoader.Load(info.Icon);
|
||||||
Text.Text = instance?.Name ?? info.Name;
|
Text.Text = instance?.Name ?? info.Name;
|
||||||
Button.Click += (o, args) => Clicked?.Invoke(this, EventArgs.Empty);
|
Button.Click += (o, args) => Clicked?.Invoke(this, EventArgs.Empty);
|
||||||
Button.ToolTip = instance?.Name ?? info.Tooltip;
|
Button.ToolTip = instance?.Name ?? info.Tooltip;
|
||||||
|
|
|
@ -37,7 +37,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
|
||||||
application.InstanceStarted += Application_InstanceStarted;
|
application.InstanceStarted += Application_InstanceStarted;
|
||||||
|
|
||||||
Button.Click += Button_Click;
|
Button.Click += Button_Click;
|
||||||
Button.Content = IconResourceLoader.Load(application.Info.IconResource);
|
Button.Content = IconResourceLoader.Load(application.Info.Icon);
|
||||||
Button.MouseEnter += (o, args) => InstancePopup.IsOpen = InstanceStackPanel.Children.Count > 1;
|
Button.MouseEnter += (o, args) => InstancePopup.IsOpen = InstanceStackPanel.Children.Count > 1;
|
||||||
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => InstancePopup.IsOpen = InstancePopup.IsMouseOver));
|
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => InstancePopup.IsOpen = InstancePopup.IsMouseOver));
|
||||||
Button.ToolTip = application.Info.Tooltip;
|
Button.ToolTip = application.Info.Tooltip;
|
||||||
|
|
|
@ -32,7 +32,7 @@ namespace SafeExamBrowser.UserInterface.Desktop.Controls
|
||||||
{
|
{
|
||||||
Button.Click += Button_Click;
|
Button.Click += Button_Click;
|
||||||
Button.ToolTip = instance.Name;
|
Button.ToolTip = instance.Name;
|
||||||
Icon.Content = IconResourceLoader.Load(info.IconResource);
|
Icon.Content = IconResourceLoader.Load(info.Icon);
|
||||||
instance.IconChanged += Instance_IconChanged;
|
instance.IconChanged += Instance_IconChanged;
|
||||||
instance.NameChanged += Instance_NameChanged;
|
instance.NameChanged += Instance_NameChanged;
|
||||||
Text.Text = instance.Name;
|
Text.Text = instance.Name;
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop"
|
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Desktop"
|
||||||
mc:Ignorable="d" AllowsTransparency="True" Background="#74000000" BorderBrush="DodgerBlue" BorderThickness="1" Title="TaskView"
|
mc:Ignorable="d" AllowsTransparency="True" Background="#AA000000" BorderBrush="DodgerBlue" BorderThickness="1" Title="TaskView"
|
||||||
Height="450" Width="800" WindowStartupLocation="CenterScreen" WindowStyle="None">
|
Topmost="True" Height="450" SizeToContent="WidthAndHeight" Width="800" WindowStartupLocation="CenterScreen" WindowStyle="None">
|
||||||
<Grid>
|
<Grid>
|
||||||
|
<StackPanel Name="Rows" Margin="10" Orientation="Vertical" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Window>
|
</Window>
|
||||||
|
|
|
@ -6,27 +6,156 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Documents;
|
||||||
|
using System.Windows.Media;
|
||||||
using SafeExamBrowser.Applications.Contracts;
|
using SafeExamBrowser.Applications.Contracts;
|
||||||
|
using SafeExamBrowser.Core.Contracts;
|
||||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||||
|
using SafeExamBrowser.UserInterface.Shared.Utilities;
|
||||||
|
|
||||||
namespace SafeExamBrowser.UserInterface.Desktop
|
namespace SafeExamBrowser.UserInterface.Desktop
|
||||||
{
|
{
|
||||||
public partial class TaskView : Window, ITaskView
|
public partial class TaskView : Window, ITaskView
|
||||||
{
|
{
|
||||||
|
private IList<IApplicationInstance> instances;
|
||||||
|
|
||||||
public TaskView()
|
public TaskView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
instances = new List<IApplicationInstance>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Add(IApplication application)
|
public void Add(IApplication application)
|
||||||
{
|
{
|
||||||
|
application.InstanceStarted += Application_InstanceStarted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Register(ITaskViewActivator activator)
|
public void Register(ITaskViewActivator activator)
|
||||||
{
|
{
|
||||||
|
activator.Deactivated += Activator_Deactivated;
|
||||||
|
activator.NextActivated += Activator_Next;
|
||||||
|
activator.PreviousActivated += Activator_Previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Application_InstanceStarted(IApplicationInstance instance)
|
||||||
|
{
|
||||||
|
Dispatcher.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
instance.IconChanged += Instance_IconChanged;
|
||||||
|
instance.NameChanged += Instance_NameChanged;
|
||||||
|
instance.Terminated += Instance_Terminated;
|
||||||
|
|
||||||
|
instances.Add(instance);
|
||||||
|
Update();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Activator_Deactivated()
|
||||||
|
{
|
||||||
|
Dispatcher.InvokeAsync(Hide);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Activator_Next()
|
||||||
|
{
|
||||||
|
Dispatcher.InvokeAsync(ShowConditional);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Activator_Previous()
|
||||||
|
{
|
||||||
|
Dispatcher.InvokeAsync(ShowConditional);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Instance_IconChanged(IconResource icon)
|
||||||
|
{
|
||||||
|
// TODO Dispatcher.InvokeAsync(...);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Instance_NameChanged(string name)
|
||||||
|
{
|
||||||
|
// TODO Dispatcher.InvokeAsync(...);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Instance_Terminated(InstanceIdentifier id)
|
||||||
|
{
|
||||||
|
Dispatcher.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
var instance = instances.FirstOrDefault(i => i.Id == id);
|
||||||
|
|
||||||
|
if (instance != default(IApplicationInstance))
|
||||||
|
{
|
||||||
|
instances.Remove(instance);
|
||||||
|
Update();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowConditional()
|
||||||
|
{
|
||||||
|
if (Visibility != Visibility.Visible && instances.Any())
|
||||||
|
{
|
||||||
|
Show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
var max = Math.Ceiling(Math.Sqrt(instances.Count));
|
||||||
|
var stack = new Stack<IApplicationInstance>(instances);
|
||||||
|
|
||||||
|
Rows.Children.Clear();
|
||||||
|
|
||||||
|
for (var rowCount = 0; rowCount < max && stack.Any(); rowCount++)
|
||||||
|
{
|
||||||
|
var row = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Center };
|
||||||
|
|
||||||
|
Rows.Children.Add(row);
|
||||||
|
|
||||||
|
for (var columnIndex = 0; columnIndex < max && stack.Any(); columnIndex++)
|
||||||
|
{
|
||||||
|
var instance = stack.Pop();
|
||||||
|
var control = BuildInstanceControl(instance);
|
||||||
|
|
||||||
|
row.Children.Add(control);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateLayout();
|
||||||
|
|
||||||
|
Left = (SystemParameters.WorkArea.Width - Width) / 2 + SystemParameters.WorkArea.Left;
|
||||||
|
Top = (SystemParameters.WorkArea.Height - Height) / 2 + SystemParameters.WorkArea.Top;
|
||||||
|
|
||||||
|
if (!instances.Any())
|
||||||
|
{
|
||||||
|
Hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private UIElement BuildInstanceControl(IApplicationInstance instance)
|
||||||
|
{
|
||||||
|
var border = new Border();
|
||||||
|
var stackPanel = new StackPanel();
|
||||||
|
var icon = IconResourceLoader.Load(instance.Icon);
|
||||||
|
|
||||||
|
border.BorderBrush = Brushes.White;
|
||||||
|
border.BorderThickness = new Thickness(1);
|
||||||
|
border.Height = 150;
|
||||||
|
border.Margin = new Thickness(5);
|
||||||
|
border.Padding = new Thickness(2);
|
||||||
|
border.Width = 250;
|
||||||
|
border.Child = stackPanel;
|
||||||
|
|
||||||
|
stackPanel.HorizontalAlignment = HorizontalAlignment.Center;
|
||||||
|
stackPanel.Orientation = Orientation.Vertical;
|
||||||
|
stackPanel.VerticalAlignment = VerticalAlignment.Center;
|
||||||
|
stackPanel.Children.Add(new ContentControl { Content = icon, MaxWidth = 50 });
|
||||||
|
stackPanel.Children.Add(new TextBlock(new Run($"Instance {instance.Name ?? "NULL"}") { Foreground = Brushes.White, FontWeight = FontWeights.Bold }));
|
||||||
|
|
||||||
|
return border;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
|
||||||
|
|
||||||
private void InitializeApplicationInstanceButton()
|
private void InitializeApplicationInstanceButton()
|
||||||
{
|
{
|
||||||
Icon.Content = IconResourceLoader.Load(info.IconResource);
|
Icon.Content = IconResourceLoader.Load(info.Icon);
|
||||||
Text.Text = instance?.Name ?? info.Name;
|
Text.Text = instance?.Name ?? info.Name;
|
||||||
Button.Click += (o, args) => Clicked?.Invoke(this, EventArgs.Empty);
|
Button.Click += (o, args) => Clicked?.Invoke(this, EventArgs.Empty);
|
||||||
Button.ToolTip = instance?.Name ?? info.Tooltip;
|
Button.ToolTip = instance?.Name ?? info.Tooltip;
|
||||||
|
|
|
@ -37,7 +37,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
|
||||||
application.InstanceStarted += Application_InstanceStarted;
|
application.InstanceStarted += Application_InstanceStarted;
|
||||||
|
|
||||||
Button.Click += Button_Click;
|
Button.Click += Button_Click;
|
||||||
Button.Content = IconResourceLoader.Load(application.Info.IconResource);
|
Button.Content = IconResourceLoader.Load(application.Info.Icon);
|
||||||
Button.MouseEnter += (o, args) => InstancePopup.IsOpen = InstanceStackPanel.Children.Count > 1;
|
Button.MouseEnter += (o, args) => InstancePopup.IsOpen = InstanceStackPanel.Children.Count > 1;
|
||||||
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => InstancePopup.IsOpen = InstancePopup.IsMouseOver));
|
Button.MouseLeave += (o, args) => Task.Delay(250).ContinueWith(_ => Dispatcher.Invoke(() => InstancePopup.IsOpen = InstancePopup.IsMouseOver));
|
||||||
Button.ToolTip = application.Info.Tooltip;
|
Button.ToolTip = application.Info.Tooltip;
|
||||||
|
|
|
@ -32,7 +32,7 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
|
||||||
{
|
{
|
||||||
Button.Click += Button_Click;
|
Button.Click += Button_Click;
|
||||||
Button.ToolTip = instance.Name;
|
Button.ToolTip = instance.Name;
|
||||||
Icon.Content = IconResourceLoader.Load(info.IconResource);
|
Icon.Content = IconResourceLoader.Load(info.Icon);
|
||||||
instance.IconChanged += Instance_IconChanged;
|
instance.IconChanged += Instance_IconChanged;
|
||||||
instance.NameChanged += Instance_NameChanged;
|
instance.NameChanged += Instance_NameChanged;
|
||||||
Text.Text = instance.Name;
|
Text.Text = instance.Name;
|
||||||
|
|
|
@ -17,10 +17,12 @@ namespace SafeExamBrowser.UserInterface.Shared.Activators
|
||||||
{
|
{
|
||||||
public class TaskViewKeyboardActivator : KeyboardActivator, ITaskViewActivator
|
public class TaskViewKeyboardActivator : KeyboardActivator, ITaskViewActivator
|
||||||
{
|
{
|
||||||
|
private bool Activated, BlockActivation, LeftShift, Tab;
|
||||||
private ILogger logger;
|
private ILogger logger;
|
||||||
|
|
||||||
public event ActivatorEventHandler Next;
|
public event ActivatorEventHandler Deactivated;
|
||||||
public event ActivatorEventHandler Previous;
|
public event ActivatorEventHandler NextActivated;
|
||||||
|
public event ActivatorEventHandler PreviousActivated;
|
||||||
|
|
||||||
public TaskViewKeyboardActivator(ILogger logger, INativeMethods nativeMethods) : base(nativeMethods)
|
public TaskViewKeyboardActivator(ILogger logger, INativeMethods nativeMethods) : base(nativeMethods)
|
||||||
{
|
{
|
||||||
|
@ -29,17 +31,82 @@ namespace SafeExamBrowser.UserInterface.Shared.Activators
|
||||||
|
|
||||||
public void Pause()
|
public void Pause()
|
||||||
{
|
{
|
||||||
Paused = true;
|
BlockActivation = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Resume()
|
public void Resume()
|
||||||
{
|
{
|
||||||
Paused = false;
|
BlockActivation = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool Process(Key key, KeyModifier modifier, KeyState state)
|
protected override bool Process(Key key, KeyModifier modifier, KeyState state)
|
||||||
{
|
{
|
||||||
|
if (IsDeactivation(modifier))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsActivation(key, modifier, state))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool IsActivation(Key key, KeyModifier modifier, KeyState state)
|
||||||
|
{
|
||||||
|
var changed = false;
|
||||||
|
var pressed = state == KeyState.Pressed && modifier.HasFlag(KeyModifier.Alt);
|
||||||
|
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
case Key.Tab:
|
||||||
|
changed = Tab != pressed;
|
||||||
|
Tab = pressed;
|
||||||
|
break;
|
||||||
|
case Key.LeftShift:
|
||||||
|
changed = LeftShift != pressed;
|
||||||
|
LeftShift = pressed;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var isActivation = Tab && changed;
|
||||||
|
|
||||||
|
if (isActivation && !BlockActivation)
|
||||||
|
{
|
||||||
|
Activated = true;
|
||||||
|
|
||||||
|
if (LeftShift)
|
||||||
|
{
|
||||||
|
logger.Debug("Detected sequence for previous instance.");
|
||||||
|
PreviousActivated?.Invoke();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Debug("Detected sequence for next instance.");
|
||||||
|
NextActivated?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isActivation;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsDeactivation(KeyModifier modifier)
|
||||||
|
{
|
||||||
|
var isDeactivation = Activated && !modifier.HasFlag(KeyModifier.Alt);
|
||||||
|
|
||||||
|
if (isDeactivation)
|
||||||
|
{
|
||||||
|
Activated = false;
|
||||||
|
LeftShift = false;
|
||||||
|
Tab = false;
|
||||||
|
|
||||||
|
logger.Debug("Detected deactivation sequence.");
|
||||||
|
Deactivated?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
return isDeactivation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue