SEBWIN-312: Implemented thumbnails of open windows for mobile taskview.

This commit is contained in:
dbuechel 2019-12-05 12:35:59 +01:00
parent a93678d5d7
commit 7cbf9c39d3
6 changed files with 206 additions and 57 deletions

View file

@ -309,7 +309,7 @@ namespace SafeExamBrowser.Client
switch (uiMode)
{
case UserInterfaceMode.Mobile:
return new Mobile.TaskView();
return new Mobile.Taskview();
default:
return new Desktop.Taskview();
}

View file

@ -1,9 +1,10 @@
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.TaskViewWindowControl"
<UserControl x:Class="SafeExamBrowser.UserInterface.Mobile.Controls.TaskviewWindowControl"
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">
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile.Controls"
FontSize="16">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
@ -11,11 +12,20 @@
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Margin="5" Height="150" Width="250">
<Grid Margin="5" Height="200" Width="300">
<Border Name="Indicator" Background="{StaticResource BackgroundTransparentEmphasisBrush}" BorderThickness="0" Visibility="Hidden" />
<StackPanel HorizontalAlignment="Center" Orientation="Vertical" VerticalAlignment="Center">
<ContentControl Name="Icon" MaxWidth="40" />
<TextBlock Name="Title" Margin="10" Foreground="{StaticResource PrimaryTextBrush}" TextTrimming="CharacterEllipsis" />
</StackPanel>
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" Grid.Column="0" Name="Icon" Margin="0,0,5,0" Height="24" />
<TextBlock Grid.Row="0" Grid.Column="1" Name="Title" Foreground="{StaticResource PrimaryTextBrush}" VerticalAlignment="Center" TextTrimming="CharacterEllipsis" />
<ContentControl Grid.Row="1" Grid.ColumnSpan="2" Name="Placeholder" Margin="0,5,0,0" />
</Grid>
</Grid>
</UserControl>

View file

@ -6,6 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Windows;
using System.Windows.Controls;
using SafeExamBrowser.Applications.Contracts;
@ -14,13 +15,16 @@ using SafeExamBrowser.UserInterface.Shared.Utilities;
namespace SafeExamBrowser.UserInterface.Mobile.Controls
{
public partial class TaskViewWindowControl : UserControl
public partial class TaskviewWindowControl : UserControl
{
private Taskview taskview;
private IntPtr thumbnail;
private IApplicationWindow window;
public TaskViewWindowControl(IApplicationWindow window)
public TaskviewWindowControl(IApplicationWindow window, Taskview taskview)
{
this.window = window;
this.taskview = taskview;
InitializeComponent();
InitializeControl();
@ -33,35 +37,128 @@ namespace SafeExamBrowser.UserInterface.Mobile.Controls
internal void Deselect()
{
Icon.MaxWidth = 40;
Indicator.Visibility = Visibility.Hidden;
Title.FontWeight = FontWeights.Normal;
}
internal void Destroy()
{
if (thumbnail != IntPtr.Zero)
{
Thumbnail.DwmUnregisterThumbnail(thumbnail);
}
}
internal void Select()
{
Icon.MaxWidth = 50;
Indicator.Visibility = Visibility.Visible;
Title.FontWeight = FontWeights.SemiBold;
}
internal void Update()
{
if (!IsLoaded || !IsVisible)
{
return;
}
if (thumbnail == IntPtr.Zero && taskview.Handle != IntPtr.Zero && window.Handle != IntPtr.Zero)
{
Thumbnail.DwmRegisterThumbnail(taskview.Handle, window.Handle, out thumbnail);
}
if (thumbnail != IntPtr.Zero)
{
Thumbnail.DwmQueryThumbnailSourceSize(thumbnail, out var size);
var destination = CalculatePhysicalDestination(size);
var properties = new Thumbnail.Properties
{
Destination = destination,
Flags = Thumbnail.DWM_TNP_RECTDESTINATION | Thumbnail.DWM_TNP_VISIBLE,
Visible = true
};
Thumbnail.DwmUpdateThumbnailProperties(thumbnail, ref properties);
}
}
private void TaskViewWindowControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue as bool? == true)
{
Update();
}
}
private void Window_TitleChanged(string title)
{
Dispatcher.InvokeAsync(() => Title.Text = title);
}
private void Window_IconChanged(IconResource icon)
{
Dispatcher.InvokeAsync(() => Icon.Content = IconResourceLoader.Load(window.Icon));
}
private Thumbnail.Rectangle CalculatePhysicalDestination(Thumbnail.Size size)
{
var controlToTaskview = TransformToVisual(taskview);
var placeholderToControl = Placeholder.TransformToVisual(this);
var placeholderLeft = placeholderToControl.Transform(new Point(0, 0)).X;
var placeholderTop = placeholderToControl.Transform(new Point(0, 0)).Y;
var placeholderRight = placeholderToControl.Transform(new Point(Placeholder.ActualWidth, 0)).X;
var placeholderBottom = placeholderToControl.Transform(new Point(0, Placeholder.ActualHeight)).Y;
var physicalBounds = new Thumbnail.Rectangle
{
Left = (int) Math.Round(this.TransformToPhysical(controlToTaskview.Transform(new Point(placeholderLeft, 0)).X, 0).X),
Top = (int) Math.Round(this.TransformToPhysical(0, controlToTaskview.Transform(new Point(0, placeholderTop)).Y).Y),
Right = (int) Math.Round(this.TransformToPhysical(controlToTaskview.Transform(new Point(placeholderRight, 0)).X, 0).X),
Bottom = (int) Math.Round(this.TransformToPhysical(0, controlToTaskview.Transform(new Point(0, placeholderBottom)).Y).Y)
};
var scaleFactor = default(double);
var thumbnailHeight = default(double);
var thumbnailWidth = default(double);
var maxWidth = (double) physicalBounds.Right - physicalBounds.Left;
var maxHeight = (double) physicalBounds.Bottom - physicalBounds.Top;
var placeholderRatio = maxWidth / maxHeight;
var windowRatio = (double) size.X / size.Y;
if (windowRatio < placeholderRatio)
{
thumbnailHeight = maxHeight;
scaleFactor = thumbnailHeight / size.Y;
thumbnailWidth = size.X * scaleFactor;
}
else
{
thumbnailWidth = maxWidth;
scaleFactor = thumbnailWidth / size.X;
thumbnailHeight = size.Y * scaleFactor;
}
var widthDifference = maxWidth - thumbnailWidth;
var heightDifference = maxHeight - thumbnailHeight;
return new Thumbnail.Rectangle
{
Left = (int) Math.Round(physicalBounds.Left + (widthDifference / 2)),
Top = (int) Math.Round(physicalBounds.Top + (heightDifference / 2)),
Right = (int) Math.Round(physicalBounds.Right - (widthDifference / 2)),
Bottom = (int) Math.Round(physicalBounds.Bottom - (heightDifference / 2))
};
}
private void InitializeControl()
{
Icon.Content = IconResourceLoader.Load(window.Icon);
IsVisibleChanged += TaskViewWindowControl_IsVisibleChanged;
Loaded += (o, args) => Update();
Title.Text = window.Title;
window.IconChanged += Instance_IconChanged;
window.TitleChanged += Instance_TitleChanged;
}
private void Instance_TitleChanged(string title)
{
Dispatcher.InvokeAsync(() => Title.Text = title);
}
private void Instance_IconChanged(IconResource icon)
{
Dispatcher.InvokeAsync(() => Icon.Content = IconResourceLoader.Load(window.Icon));
window.IconChanged += Window_IconChanged;
window.TitleChanged += Window_TitleChanged;
}
}
}

View file

@ -143,8 +143,8 @@
<Compile Include="Controls\TaskbarWirelessNetworkControl.xaml.cs">
<DependentUpon>TaskbarWirelessNetworkControl.xaml</DependentUpon>
</Compile>
<Compile Include="Controls\TaskViewWindowControl.xaml.cs">
<DependentUpon>TaskViewWindowControl.xaml</DependentUpon>
<Compile Include="Controls\TaskviewWindowControl.xaml.cs">
<DependentUpon>TaskviewWindowControl.xaml</DependentUpon>
</Compile>
<Compile Include="LockScreen.xaml.cs">
<DependentUpon>LockScreen.xaml</DependentUpon>
@ -171,8 +171,8 @@
<Compile Include="Taskbar.xaml.cs">
<DependentUpon>Taskbar.xaml</DependentUpon>
</Compile>
<Compile Include="TaskView.xaml.cs">
<DependentUpon>TaskView.xaml</DependentUpon>
<Compile Include="Taskview.xaml.cs">
<DependentUpon>Taskview.xaml</DependentUpon>
</Compile>
<Compile Include="UserInterfaceFactory.cs" />
<Compile Include="ViewModels\DateTimeViewModel.cs" />
@ -355,7 +355,7 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Resource>
<Page Include="Controls\TaskViewWindowControl.xaml">
<Page Include="Controls\TaskviewWindowControl.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
@ -463,7 +463,7 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="TaskView.xaml">
<Page Include="Taskview.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>

View file

@ -1,11 +1,11 @@
<Window x:Class="SafeExamBrowser.UserInterface.Mobile.TaskView"
<Window x:Class="SafeExamBrowser.UserInterface.Mobile.Taskview"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile"
mc:Ignorable="d" AllowsTransparency="True" Background="{DynamicResource BackgroundTransparentBrush}" BorderBrush="DodgerBlue" BorderThickness="1"
Title="TaskView" Topmost="True" Height="450" SizeToContent="WidthAndHeight" Width="800" WindowStartupLocation="CenterScreen" WindowStyle="None">
Title="Taskview" Topmost="True" Height="450" SizeToContent="WidthAndHeight" Width="800" WindowStartupLocation="CenterScreen" WindowStyle="None">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>

View file

@ -11,24 +11,28 @@ using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
using SafeExamBrowser.Applications.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Mobile.Controls;
namespace SafeExamBrowser.UserInterface.Mobile
{
public partial class TaskView : Window, ITaskView
public partial class Taskview : Window, ITaskView
{
private IList<IApplication> applications;
private LinkedListNode<TaskViewWindowControl> current;
private LinkedList<TaskViewWindowControl> controls;
private LinkedListNode<TaskviewWindowControl> current;
private LinkedList<TaskviewWindowControl> controls;
public TaskView()
internal IntPtr Handle { get; private set; }
public Taskview()
{
applications = new List<IApplication>();
controls = new LinkedList<TaskViewWindowControl>();
controls = new LinkedList<TaskviewWindowControl>();
InitializeComponent();
InitializeTaskview();
}
public void Add(IApplication application)
@ -66,9 +70,21 @@ namespace SafeExamBrowser.UserInterface.Mobile
private void ActivateAndHide()
{
Activate();
current?.Value.Activate();
Hide();
if (IsVisible)
{
Activate();
current?.Value.Activate();
Hide();
}
}
private void InitializeTaskview()
{
Loaded += (o, args) =>
{
Handle = new WindowInteropHelper(this).Handle;
Update();
};
}
private void SelectNext()
@ -106,31 +122,37 @@ namespace SafeExamBrowser.UserInterface.Mobile
private void Update()
{
var windows = new Stack<IApplicationWindow>();
ClearTaskview();
LoadControls();
UpdateLocation();
}
foreach (var application in applications)
private void ClearTaskview()
{
foreach (var control in controls)
{
foreach (var window in application.GetWindows())
{
windows.Push(window);
}
control.Destroy();
}
var max = Math.Ceiling(Math.Sqrt(windows.Count));
controls.Clear();
Rows.Children.Clear();
}
for (var rowCount = 0; rowCount < max && windows.Any(); rowCount++)
private void LoadControls()
{
var windows = GetAllWindows();
var maxColumns = Math.Ceiling(Math.Sqrt(windows.Count));
while (windows.Any())
{
var row = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Center };
Rows.Children.Add(row);
for (var columnIndex = 0; columnIndex < max && windows.Any(); columnIndex++)
for (var column = 0; column < maxColumns && windows.Any(); column++)
{
var window = windows.Pop();
var control = new TaskViewWindowControl(window);
var control = new TaskviewWindowControl(window, this);
controls.AddLast(control);
row.Children.Add(control);
@ -139,16 +161,36 @@ namespace SafeExamBrowser.UserInterface.Mobile
current = controls.First;
current?.Value.Select();
}
UpdateLayout();
private void UpdateLocation()
{
if (controls.Any())
{
UpdateLayout();
Left = (SystemParameters.WorkArea.Width - Width) / 2 + SystemParameters.WorkArea.Left;
Top = (SystemParameters.WorkArea.Height - Height) / 2 + SystemParameters.WorkArea.Top;
if (!controls.Any())
Left = (SystemParameters.WorkArea.Width - Width) / 2 + SystemParameters.WorkArea.Left;
Top = (SystemParameters.WorkArea.Height - Height) / 2 + SystemParameters.WorkArea.Top;
}
else
{
Hide();
}
}
private Stack<IApplicationWindow> GetAllWindows()
{
var stack = new Stack<IApplicationWindow>();
foreach (var application in applications)
{
foreach (var window in application.GetWindows())
{
stack.Push(window);
}
}
return stack;
}
}
}