SEBWIN-312: Implemented thumbnails of open windows for mobile taskview.
This commit is contained in:
parent
a93678d5d7
commit
7cbf9c39d3
6 changed files with 206 additions and 57 deletions
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue