From 7cbf9c39d3114aa4d18794085a1f55e35a73f589 Mon Sep 17 00:00:00 2001 From: dbuechel Date: Thu, 5 Dec 2019 12:35:59 +0100 Subject: [PATCH] SEBWIN-312: Implemented thumbnails of open windows for mobile taskview. --- SafeExamBrowser.Client/CompositionRoot.cs | 2 +- .../Controls/TaskViewWindowControl.xaml | 24 +++- .../Controls/TaskViewWindowControl.xaml.cs | 131 +++++++++++++++--- ...afeExamBrowser.UserInterface.Mobile.csproj | 12 +- .../TaskView.xaml | 4 +- .../TaskView.xaml.cs | 90 ++++++++---- 6 files changed, 206 insertions(+), 57 deletions(-) diff --git a/SafeExamBrowser.Client/CompositionRoot.cs b/SafeExamBrowser.Client/CompositionRoot.cs index 51056978..0e6947c4 100644 --- a/SafeExamBrowser.Client/CompositionRoot.cs +++ b/SafeExamBrowser.Client/CompositionRoot.cs @@ -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(); } diff --git a/SafeExamBrowser.UserInterface.Mobile/Controls/TaskViewWindowControl.xaml b/SafeExamBrowser.UserInterface.Mobile/Controls/TaskViewWindowControl.xaml index 48f6912f..3e67479f 100644 --- a/SafeExamBrowser.UserInterface.Mobile/Controls/TaskViewWindowControl.xaml +++ b/SafeExamBrowser.UserInterface.Mobile/Controls/TaskViewWindowControl.xaml @@ -1,9 +1,10 @@ - + xmlns:local="clr-namespace:SafeExamBrowser.UserInterface.Mobile.Controls" + FontSize="16"> @@ -11,11 +12,20 @@ - + - - - - + + + + + + + + + + + + + diff --git a/SafeExamBrowser.UserInterface.Mobile/Controls/TaskViewWindowControl.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Controls/TaskViewWindowControl.xaml.cs index b039b8c1..5d8234cf 100644 --- a/SafeExamBrowser.UserInterface.Mobile/Controls/TaskViewWindowControl.xaml.cs +++ b/SafeExamBrowser.UserInterface.Mobile/Controls/TaskViewWindowControl.xaml.cs @@ -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; } } } diff --git a/SafeExamBrowser.UserInterface.Mobile/SafeExamBrowser.UserInterface.Mobile.csproj b/SafeExamBrowser.UserInterface.Mobile/SafeExamBrowser.UserInterface.Mobile.csproj index 5db927d4..4cdcfcb7 100644 --- a/SafeExamBrowser.UserInterface.Mobile/SafeExamBrowser.UserInterface.Mobile.csproj +++ b/SafeExamBrowser.UserInterface.Mobile/SafeExamBrowser.UserInterface.Mobile.csproj @@ -143,8 +143,8 @@ TaskbarWirelessNetworkControl.xaml - - TaskViewWindowControl.xaml + + TaskviewWindowControl.xaml LockScreen.xaml @@ -171,8 +171,8 @@ Taskbar.xaml - - TaskView.xaml + + Taskview.xaml @@ -355,7 +355,7 @@ MSBuild:Compile Designer - + MSBuild:Compile Designer @@ -463,7 +463,7 @@ MSBuild:Compile Designer - + MSBuild:Compile Designer diff --git a/SafeExamBrowser.UserInterface.Mobile/TaskView.xaml b/SafeExamBrowser.UserInterface.Mobile/TaskView.xaml index 5663312c..b701343e 100644 --- a/SafeExamBrowser.UserInterface.Mobile/TaskView.xaml +++ b/SafeExamBrowser.UserInterface.Mobile/TaskView.xaml @@ -1,11 +1,11 @@ - + Title="Taskview" Topmost="True" Height="450" SizeToContent="WidthAndHeight" Width="800" WindowStartupLocation="CenterScreen" WindowStyle="None"> diff --git a/SafeExamBrowser.UserInterface.Mobile/TaskView.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/TaskView.xaml.cs index a633f039..aab6a216 100644 --- a/SafeExamBrowser.UserInterface.Mobile/TaskView.xaml.cs +++ b/SafeExamBrowser.UserInterface.Mobile/TaskView.xaml.cs @@ -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 applications; - private LinkedListNode current; - private LinkedList controls; + private LinkedListNode current; + private LinkedList controls; - public TaskView() + internal IntPtr Handle { get; private set; } + + public Taskview() { applications = new List(); - controls = new LinkedList(); + controls = new LinkedList(); 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(); + 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 GetAllWindows() + { + var stack = new Stack(); + + foreach (var application in applications) + { + foreach (var window in application.GetWindows()) + { + stack.Push(window); + } + } + + return stack; + } } }