From e97641e7a18b9449c4375bed5de3f3402849dc6b Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Sun, 23 Aug 2026 17:52:23 +1000 Subject: [PATCH] Restore the window when a snapshot asks for it A focus from the queue owner set Visible, brought the form to the front and activated it. None of those restores a minimised window: the taskbar button flashes and the window stays minimised. So a viewer someone had minimised was never shown the snapshot it was being told to show, and the queue filled up out of sight. Raising is the form's own business now, which is also what makes it testable, and a maximised window is left as it is. --- .../ViewerFormRaiseTests.cs | 50 +++++++++++++++++++ .../FormsViewerWindow.cs | 4 +- src/DiffEngineViewer.Windows/ViewerForm.cs | 21 ++++++++ 3 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 src/DiffEngineViewer.Windows.Tests/ViewerFormRaiseTests.cs diff --git a/src/DiffEngineViewer.Windows.Tests/ViewerFormRaiseTests.cs b/src/DiffEngineViewer.Windows.Tests/ViewerFormRaiseTests.cs new file mode 100644 index 00000000..d02bd99d --- /dev/null +++ b/src/DiffEngineViewer.Windows.Tests/ViewerFormRaiseTests.cs @@ -0,0 +1,50 @@ +/// +/// Bringing the window up for a snapshot that has just arrived. The queue owner asks for this over +/// the socket, and it is the only thing that puts a new snapshot in front of anyone. +/// +[NotInParallel] +[TUnit.Core.Executors.STAThreadExecutor] +public class ViewerFormRaiseTests +{ + /// + /// BringToFront and Activate leave a minimised window minimised: the taskbar button flashes + /// and nothing else happens. So a viewer that had been minimised was never actually shown the + /// snapshot, and the queue filled up out of sight. + /// + [Test] + public async Task Restores_a_minimised_window() + { + using var form = new ViewerForm("title", 800, 600); + form.WindowState = FormWindowState.Minimized; + + form.Raise(); + + await Assert.That(form.WindowState).IsEqualTo(FormWindowState.Normal); + } + + /// + /// A window the reader had maximised stays maximised: it is already as visible as it gets, and + /// restoring it would be undoing something they chose. + /// + [Test] + public async Task Leaves_a_maximised_window_maximised() + { + using var form = new ViewerForm("title", 800, 600); + form.WindowState = FormWindowState.Maximized; + + form.Raise(); + + await Assert.That(form.WindowState).IsEqualTo(FormWindowState.Maximized); + } + + [Test] + public async Task Shows_a_hidden_window() + { + using var form = new ViewerForm("title", 800, 600); + form.Visible = false; + + form.Raise(); + + await Assert.That(form.Visible).IsTrue(); + } +} diff --git a/src/DiffEngineViewer.Windows/FormsViewerWindow.cs b/src/DiffEngineViewer.Windows/FormsViewerWindow.cs index 72ce838b..2c0410f3 100644 --- a/src/DiffEngineViewer.Windows/FormsViewerWindow.cs +++ b/src/DiffEngineViewer.Windows/FormsViewerWindow.cs @@ -89,9 +89,7 @@ public void Focus() return; } - form.Visible = true; - form.BringToFront(); - form.Activate(); + form.Raise(); } public bool Capture(Screen screen, int width, int height, string pngPath) diff --git a/src/DiffEngineViewer.Windows/ViewerForm.cs b/src/DiffEngineViewer.Windows/ViewerForm.cs index fe3a3456..33c950b7 100644 --- a/src/DiffEngineViewer.Windows/ViewerForm.cs +++ b/src/DiffEngineViewer.Windows/ViewerForm.cs @@ -173,6 +173,27 @@ void ScaleChrome() scrollBar.Width = SystemInformation.GetVerticalScrollBarWidthForDpi(DeviceDpi); } + /// + /// Brings the window up, for a snapshot that has just arrived and wants reading. + /// + /// The restore is the part that was missing. BringToFront and Activate leave a minimised + /// window minimised - the taskbar button flashes and nothing else happens - so a viewer that + /// had been minimised never showed the snapshot it was being asked to show, and the queue + /// filled up out of sight. + /// + /// + public void Raise() + { + Visible = true; + if (WindowState == FormWindowState.Minimized) + { + WindowState = FormWindowState.Normal; + } + + BringToFront(); + Activate(); + } + public void Apply(Screen screen) { // ScreenBuilder allocates a fresh Screen every frame, so record equality would never hit.