diff --git a/pwiz_tools/Shared/Lib/DigitalRune.Windows.Docking.dll b/pwiz_tools/Shared/Lib/DigitalRune.Windows.Docking.dll
index 40ed14a62e..62ad0f85f7 100644
Binary files a/pwiz_tools/Shared/Lib/DigitalRune.Windows.Docking.dll and b/pwiz_tools/Shared/Lib/DigitalRune.Windows.Docking.dll differ
diff --git a/pwiz_tools/Skyline/Controls/Databinding/DocumentGridViewContext.cs b/pwiz_tools/Skyline/Controls/Databinding/DocumentGridViewContext.cs
index a5eef0c071..44e29f4a08 100644
--- a/pwiz_tools/Skyline/Controls/Databinding/DocumentGridViewContext.cs
+++ b/pwiz_tools/Skyline/Controls/Databinding/DocumentGridViewContext.cs
@@ -20,6 +20,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
+using pwiz.Common;
using pwiz.Common.DataBinding;
using pwiz.Common.DataBinding.Controls;
using pwiz.Common.DataBinding.Controls.Editor;
@@ -50,6 +51,9 @@ protected override ViewEditor CreateViewEditor(ViewGroup viewGroup, ViewSpec vie
#else
viewEditor.ShowSourceTab = false;
#endif
+ if (CommonApplicationSettings.PauseSeconds != 0)
+ viewEditor.ShowSourceTab = false; // not when taking screenshots
+
if (EnablePreview)
{
viewEditor.PreviewButtonVisible = true;
diff --git a/pwiz_tools/Skyline/Controls/Graphs/AllChromatogramsGraph.cs b/pwiz_tools/Skyline/Controls/Graphs/AllChromatogramsGraph.cs
index a112d77f71..eed7e8cfa4 100644
--- a/pwiz_tools/Skyline/Controls/Graphs/AllChromatogramsGraph.cs
+++ b/pwiz_tools/Skyline/Controls/Graphs/AllChromatogramsGraph.cs
@@ -159,6 +159,9 @@ protected override void Dispose(bool disposing)
private void ElapsedTimer_Tick(object sender, EventArgs e)
{
+ if (IsProgressFrozen())
+ return;
+
// Update timer and overall progress bar.
// ReSharper disable LocalizableElement
lblDuration.Text = _stopwatch.Elapsed.ToString(@"hh\:mm\:ss");
@@ -376,8 +379,34 @@ private void WindowMove(object sender, EventArgs e)
///
/// Display chromatogram data.
///
- ///
+ /// The to update the UI to.
public void UpdateStatus(MultiProgressStatus status)
+ {
+ lock (_missedProgressStatusList)
+ {
+ // If a freeze percent is set, freeze once all status is
+ if (IsProgressFrozen(status))
+ {
+ // Play this back later when progress is unfrozen
+ _missedProgressStatusList.Add(status);
+ lblDuration.Text = _elapsedTimeAtFreeze;
+ return;
+ }
+ if (_missedProgressStatusList.Count > 0)
+ {
+ // Play the missed progress before the current status
+ foreach (var multiProgressStatus in _missedProgressStatusList)
+ {
+ UpdateStatusInternal(multiProgressStatus);
+ }
+ _missedProgressStatusList.Clear();
+ }
+ }
+
+ UpdateStatusInternal(status);
+ }
+
+ private void UpdateStatusInternal(MultiProgressStatus status)
{
// Update overall progress bar.
if (_partialProgressList.Count == 0)
@@ -752,6 +781,39 @@ private void btnCopyText_Click(object sender, EventArgs e)
#region Testing Support
+ private int? _freezeProgressPercent;
+ private string _elapsedTimeAtFreeze;
+ private List _missedProgressStatusList = new List();
+
+ ///
+ /// Provide enough information for a consistent screenshot. Set values to null to resume.
+ ///
+ /// Percent to freeze at when all processing threads match this percent or greater
+ /// Text for an elapsed time during the freeze
+ public void SetFreezeProgressPercent(int? percent, string elapsedTime)
+ {
+ lock (_missedProgressStatusList)
+ {
+ _freezeProgressPercent = percent;
+ _elapsedTimeAtFreeze = elapsedTime;
+ }
+ }
+
+ public bool IsProgressFrozen(MultiProgressStatus status = null)
+ {
+ lock (_missedProgressStatusList)
+ {
+ if (!_freezeProgressPercent.HasValue)
+ return false;
+
+ if (status == null)
+ return _missedProgressStatusList.Count > 0;
+
+ // Stop when anything goes over the limit
+ return status.ProgressList.Any(loadingStatus => loadingStatus.PercentComplete > _freezeProgressPercent);
+ }
+ }
+
public int ProgressTotalPercent
{
get
diff --git a/pwiz_tools/Skyline/FileUI/PeptideSearch/ImportFastaControl.cs b/pwiz_tools/Skyline/FileUI/PeptideSearch/ImportFastaControl.cs
index 35d8ad4469..811d6c7e3f 100644
--- a/pwiz_tools/Skyline/FileUI/PeptideSearch/ImportFastaControl.cs
+++ b/pwiz_tools/Skyline/FileUI/PeptideSearch/ImportFastaControl.cs
@@ -296,7 +296,12 @@ private void tbxFasta_TextChanged(object sender, EventArgs e)
FastaFile = tbxFasta.Text;
if (!File.Exists(FastaFile))
ImportFastaHelper.ShowFastaError(Resources.ToolDescription_RunTool_File_not_found_);
+ }
}
+
+ public void ScrollFastaTextToEnd()
+ {
+ tbxFasta.Select(tbxFasta.Text.Length, 0);
}
public void SetFastaContent(string fastaFilePath, bool forceFastaAsFilepath = false)
diff --git a/pwiz_tools/Skyline/FileUI/PeptideSearch/ImportResultsDIAControl.cs b/pwiz_tools/Skyline/FileUI/PeptideSearch/ImportResultsDIAControl.cs
index 7d4c8ddac5..a06146558d 100644
--- a/pwiz_tools/Skyline/FileUI/PeptideSearch/ImportResultsDIAControl.cs
+++ b/pwiz_tools/Skyline/FileUI/PeptideSearch/ImportResultsDIAControl.cs
@@ -44,6 +44,14 @@ public ImportResultsDIAControl(IModifyDocumentContainer documentContainer, strin
listResultsFiles.DisplayMember = @"Name";
SimultaneousFiles = Settings.Default.ImportResultsSimultaneousFiles;
DoAutoRetry = Settings.Default.ImportResultsDoAutoRetry;
+
+ // Hide the GPF checkbox during screenshots until we branch for 24.1 docs
+ if (Program.PauseSeconds != 0)
+ {
+ btnBrowse.Top = cbGpf.Top;
+ btnRemove.Top = cbGpf.Top;
+ cbGpf.Visible = false;
+ }
}
private BindingList _foundResultsFiles;
diff --git a/pwiz_tools/Skyline/Skyline.cs b/pwiz_tools/Skyline/Skyline.cs
index 8fb859c9a0..66cf6238bb 100644
--- a/pwiz_tools/Skyline/Skyline.cs
+++ b/pwiz_tools/Skyline/Skyline.cs
@@ -4075,8 +4075,12 @@ private void UpdateProgressUI(object sender = null, EventArgs e = null)
if (!ImportingResultsWindow.IsUserCanceled)
Settings.Default.AutoShowAllChromatogramsGraph = ImportingResultsWindow.Visible;
ImportingResultsWindow.Finish();
- if (!ImportingResultsWindow.HasErrors && Settings.Default.ImportResultsAutoCloseWindow)
+ if (!ImportingResultsWindow.HasErrors &&
+ !ImportingResultsWindow.IsProgressFrozen() &&
+ Settings.Default.ImportResultsAutoCloseWindow)
+ {
DestroyAllChromatogramsGraph();
+ }
}
}
diff --git a/pwiz_tools/Skyline/SkylineTester/Main.cs b/pwiz_tools/Skyline/SkylineTester/Main.cs
index 4e1e3ffe52..8632ee38d3 100644
--- a/pwiz_tools/Skyline/SkylineTester/Main.cs
+++ b/pwiz_tools/Skyline/SkylineTester/Main.cs
@@ -364,9 +364,10 @@ private IEnumerable GetLanguageNames()
{
foreach (var language in GetLanguages())
{
- string name;
- if (_languageNames.TryGetValue(language, out name))
- yield return name;
+ yield return _languageNames
+ .Where(lang => lang.Key.StartsWith(language))
+ .Select(lang => lang.Value)
+ .First();
}
}
diff --git a/pwiz_tools/Skyline/SkylineTester/SkylineTesterWindow.cs b/pwiz_tools/Skyline/SkylineTester/SkylineTesterWindow.cs
index 0eb24555d7..dd8fcb6984 100644
--- a/pwiz_tools/Skyline/SkylineTester/SkylineTesterWindow.cs
+++ b/pwiz_tools/Skyline/SkylineTester/SkylineTesterWindow.cs
@@ -87,9 +87,9 @@ public Button DefaultButton
private readonly Dictionary _languageNames = new Dictionary
{
- {"en", "English"},
- {"fr", "French"},
- {"tr", "Turkish"},
+ {"en-US", "English"},
+ {"fr-FR", "French"},
+ {"tr-TR", "Turkish"},
{"ja", "Japanese"},
{"zh-CHS", "Chinese"}
};
@@ -390,6 +390,9 @@ private void BackgroundLoad(object sender, DoWorkEventArgs e)
RunUI(() =>
{
+ if (!Equals(testSet.SelectedItem, testSetValue))
+ return;
+
testsTree.Nodes.Clear();
testsTree.Nodes.Add(skylineNode);
skylineNode.Expand();
diff --git a/pwiz_tools/Skyline/TestPerf/DdaTutorialTest.cs b/pwiz_tools/Skyline/TestPerf/DdaTutorialTest.cs
index b7535f4d4d..87653c946e 100644
--- a/pwiz_tools/Skyline/TestPerf/DdaTutorialTest.cs
+++ b/pwiz_tools/Skyline/TestPerf/DdaTutorialTest.cs
@@ -206,6 +206,7 @@ private void TestMsFraggerSearch()
Assert.IsFalse(importPeptideSearchDlg.ImportFastaControl.DecoyGenerationEnabled);
importPeptideSearchDlg.ImportFastaControl.MaxMissedCleavages = 0;
importPeptideSearchDlg.ImportFastaControl.SetFastaContent(GetTestPath("DdaSearchMs1Filtering\\2014_01_HUMAN_UPS.fasta"));
+ importPeptideSearchDlg.ImportFastaControl.ScrollFastaTextToEnd(); // So that the FASTA file name is visible
//importPeptideSearchDlg.ImportFastaControl.SetFastaContent(@"D:\test\Skyline\downloads\Tutorials\DdaSearchMs1Filtering\DdaSearchMS1Filtering\2021-11-09-decoys-2014_01_HUMAN_UPS.fasta");
});
PauseForScreenShot("Import Peptide Search - Import FASTA page", tutorialPage++);
diff --git a/pwiz_tools/Skyline/TestPerf/DiaSwathTutorialTest.cs b/pwiz_tools/Skyline/TestPerf/DiaSwathTutorialTest.cs
index e2e4ad2185..d87298e734 100644
--- a/pwiz_tools/Skyline/TestPerf/DiaSwathTutorialTest.cs
+++ b/pwiz_tools/Skyline/TestPerf/DiaSwathTutorialTest.cs
@@ -23,6 +23,8 @@
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Threading;
+using System.Windows.Forms;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using pwiz.Common.Chemistry;
using pwiz.Common.DataBinding;
@@ -587,6 +589,13 @@ protected override void DoTest()
Assert.IsFalse(importPeptideSearchDlg.BuildPepSearchLibControl.IncludeAmbiguousMatches);
});
WaitForConditionUI(() => importPeptideSearchDlg.IsNextButtonEnabled);
+ RunUIForScreenShot(() =>
+ {
+ var cols = importPeptideSearchDlg.BuildPepSearchLibControl.Grid.Columns;
+ cols[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
+ cols[0].Width = 175; // just "interact.pep.xml"
+ cols[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill; // To show the full PeptideProphet confidence
+ });
PauseForScreenShot("Import Peptide Search - Build Spectral Library populated page", screenshotPage++);
AddIrtPeptidesDlg addIrtPeptidesDlg;
@@ -852,8 +861,10 @@ protected override void DoTest()
OkDialog(peptidesPerProteinDlg, peptidesPerProteinDlg.OkDialog);
var allChrom = WaitForOpenForm();
- WaitForConditionUI(() => allChrom.ProgressTotalPercent >= 20);
+ allChrom.SetFreezeProgressPercent(41, @"00:00:22");
+ WaitForCondition(() => allChrom.IsProgressFrozen());
PauseForScreenShot("Loading chromatograms window", screenshotPage++, 30*1000); // 30 second timeout to avoid getting stuck
+ allChrom.SetFreezeProgressPercent(null, null);
WaitForDocumentChangeLoaded(doc, 20 * 60 * 1000); // 20 minutes
var peakScoringModelDlg = WaitForOpenForm();
@@ -937,6 +948,11 @@ protected override void DoTest()
RunUI(() =>
{
SkylineWindow.Size = new Size(900, 900);
+ SkylineWindow.ForceOnScreen(); // Avoid this shifting the window under the floating window later
+ });
+ Thread.Sleep(200); // Give layout time to adjust
+ RunUI(() =>
+ {
var chromPane1 = SkylineWindow.GetGraphChrom(SkylineWindow.Document.Settings.MeasuredResults.Chromatograms[0].Name);
var chromPane2 = SkylineWindow.GetGraphChrom(SkylineWindow.Document.Settings.MeasuredResults.Chromatograms[1].Name);
var rtGraphFrame = FindFloatingWindow(SkylineWindow.GraphRetentionTime);
diff --git a/pwiz_tools/Skyline/TestPerf/HiResMetabolomicsTutorial.cs b/pwiz_tools/Skyline/TestPerf/HiResMetabolomicsTutorial.cs
index b8037afb43..2550d3d5d0 100644
--- a/pwiz_tools/Skyline/TestPerf/HiResMetabolomicsTutorial.cs
+++ b/pwiz_tools/Skyline/TestPerf/HiResMetabolomicsTutorial.cs
@@ -163,14 +163,48 @@ protected override void DoTest()
});
RestoreViewOnScreen(5);
- PauseForScreenShot("Skyline with 14 transition - show the right-click menu for setting DHA to be a surrogate standard", 9);
+ PauseForScreenShot("Skyline with 14 transition", 9);
// Set the standard type of the surrogate standards to StandardType.SURROGATE_STANDARD
SelectNode(SrmDocument.Level.Molecules, 3);
- // TODO: Show right-click menu with "Surrogate Standard" selected
+ if (IsPauseForScreenShots)
+ {
+ RunUI(() => SkylineWindow.Height = 720); // Taller for context menu
+
+ var sequenceTree = SkylineWindow.SequenceTree;
+ ToolStripDropDown menuStrip = null, subMenuStrip = null;
+
+ RunUI(() =>
+ {
+ var rectSelectedItem = sequenceTree.SelectedNode.Bounds;
+ SkylineWindow.ContextMenuTreeNode.Show(sequenceTree.PointToScreen(
+ new Point(rectSelectedItem.X + rectSelectedItem.Width / 2,
+ rectSelectedItem.Y + rectSelectedItem.Height / 2)));
+ var setStandardTypeMenu = SkylineWindow.ContextMenuTreeNode.Items.OfType()
+ .First(i => Equals(i.Name, @"setStandardTypeContextMenuItem"));
+ setStandardTypeMenu.ShowDropDown();
+ setStandardTypeMenu.DropDownItems.OfType()
+ .First(i => Equals(i.Name, @"surrogateStandardContextMenuItem")).Select();
+
+ menuStrip = SkylineWindow.ContextMenuTreeNode;
+ subMenuStrip = setStandardTypeMenu.DropDown;
+ menuStrip.Closing += DenyMenuClosing;
+ subMenuStrip.Closing += DenyMenuClosing;
+ });
+
+ // Should all land on the SkylineWindow, so just screenshot the whole window
+ PauseForScreenShot("Skyline with 4 molecules with menu and submenu showing for surrogate standard setting");
+
+ RunUI(() =>
+ {
+ menuStrip.Closing -= DenyMenuClosing;
+ subMenuStrip.Closing -= DenyMenuClosing;
+ menuStrip.Close();
+ });
+ }
+
RunUI(() => SkylineWindow.SetStandardType(StandardType.SURROGATE_STANDARD));
- PauseForScreenShot("Skyline with 4 molecules and one set as surrogate standard");
RunUI(() => SkylineWindow.SaveDocument(GetTestPath("FattyAcids_demo.sky")));
using (new WaitDocumentChange(1, true))
diff --git a/pwiz_tools/Skyline/TestRunner/Program.cs b/pwiz_tools/Skyline/TestRunner/Program.cs
index 3583e9cb29..893621d53d 100644
--- a/pwiz_tools/Skyline/TestRunner/Program.cs
+++ b/pwiz_tools/Skyline/TestRunner/Program.cs
@@ -61,8 +61,8 @@ internal static class Program
private static readonly string[] TEST_DLLS = { "Test.dll", "TestData.dll", "TestConnected.dll", "TestFunctional.dll", "TestTutorial.dll", "CommonTest.dll", "TestPerf.dll" };
private static readonly string executingDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
- private static readonly string[] allLanguages = new FindLanguages(executingDirectory, "en", "fr", "tr").Enumerate().ToArray(); // Languages used in pass 1, and in pass 2 perftets
- private static readonly string[] qualityLanguages = new FindLanguages(executingDirectory, "en", "fr").Enumerate().ToArray(); // "fr" and "tr" pretty much test the same thing, so just use fr in pass 2
+ private static readonly string[] allLanguages = new FindLanguages(executingDirectory, "en-US", "fr-FR", "tr-TR").Enumerate().ToArray(); // Languages used in pass 1, and in pass 2 perftets
+ private static readonly string[] qualityLanguages = allLanguages.Where(l => !l.StartsWith("tr")).ToArray(); // "fr" and "tr" pretty much test the same thing, so just use fr in pass 2
private const int LeakTrailingDeltas = 7; // Number of trailing deltas to average and check against thresholds below
// CONSIDER: Ideally these thresholds would be zero, but memory and handle retention are not stable enough to support that
@@ -896,7 +896,7 @@ private static bool PushToTestQueue(List testList, List unfi
if (commandLineArgs.ArgAsBool("buildcheck"))
{
loop = 1;
- languages = new[] { "en" };
+ languages = new[] { "en-US" };
}
Action LogTestOutput = (testOutput, testLog, pass) =>
@@ -1285,7 +1285,19 @@ private static string[] GetLanguages(CommandLineArgs args)
string value = args.ArgAsString("language");
if (value == "all")
return allLanguages;
- return value.Split(',');
+ return value.Split(',').Select(GetCanonicalLanguage).ToArray();
+ }
+
+ private static string GetCanonicalLanguage(string rawLanguage)
+ {
+ // If the raw language is a prefix of something from allLanguages, use
+ // the full name.
+ foreach (var language in allLanguages)
+ {
+ if (language.StartsWith(rawLanguage))
+ return language;
+ }
+ return rawLanguage;
}
private static DirectoryInfo GetSkylineDirectory()
@@ -1492,7 +1504,7 @@ private static bool RunTestPasses(
// Get list of languages
var languages = buildMode
- ? new[] { "en" }
+ ? new[] { "en-US" }
: GetLanguages(commandLineArgs);
if (showFormNames)
diff --git a/pwiz_tools/Skyline/TestTutorial/CustomReportsTutorialTest.cs b/pwiz_tools/Skyline/TestTutorial/CustomReportsTutorialTest.cs
index 8ef4c9a486..f816f151ec 100644
--- a/pwiz_tools/Skyline/TestTutorial/CustomReportsTutorialTest.cs
+++ b/pwiz_tools/Skyline/TestTutorial/CustomReportsTutorialTest.cs
@@ -359,49 +359,22 @@ protected bool DoQualityControlSummaryReports()
ConfigureDataGridColumns();
});
- PauseForScreenShot("Document Grid with summary statistics", 21, processShot: (bmp) =>
+ PauseForScreenShot("Document Grid with summary statistics", 21, processShot: bmp =>
{
- const int lineWidth = 3;
- var dataGridView = documentGridForm.DataGridView;
+ // Clean-up the border in the normal way
+ bmp = bmp.CleanupBorder(true);
- // compute top-left corner of data grid's cells, 4px offset puts shapes in the correct place
- var yOffset = documentGridForm.NavBar.Height + dataGridView.ColumnHeadersHeight - 4;
-
- // keep these methods private until we decide to promote them to shared helpers
- var drawBoxOnColumn = new Action((graphics, column, color) =>
- {
- var rect = dataGridView.GetCellDisplayRectangle(column, 0, true); // column's top data cell
- rect.Y += yOffset;
- rect.Height *= 10; // draw rectangle around all 10 rows
-
- graphics.DrawRectangle(new Pen(color, lineWidth), rect);
- });
-
- var drawEllipseOnCell = new Action((graphics, row, column, color) =>
- {
- var text = dataGridView.Rows[row].Cells[column].FormattedValue?.ToString();
- var stringSize = graphics.MeasureString(text, dataGridView.Font);
-
- var rect = dataGridView.GetCellDisplayRectangle(column, row, true);
- rect.Y += yOffset;
- rect.Width = Convert.ToInt16(stringSize.Width * 1.1); // scale-up ellipse size so shape isn't too tight around text
-
- graphics.DrawEllipse(new Pen(color, lineWidth), rect);
- });
-
- var g = Graphics.FromImage(bmp);
+ using var g = Graphics.FromImage(bmp);
g.SmoothingMode = SmoothingMode.AntiAlias;
- drawBoxOnColumn(g, 3, Color.Red);
- drawBoxOnColumn(g, 5, Color.Red);
- drawBoxOnColumn(g, 6, Color.Red);
-
- drawEllipseOnCell(g, 1, 3, Color.Red);
- drawEllipseOnCell(g, 3, 3, Color.Orange);
- drawEllipseOnCell(g, 1, 5, Color.Orange);
- drawEllipseOnCell(g, 3, 5, Color.Orange);
+ g.DrawBoxOnColumn(documentGridForm, 3, 10, Color.Red);
+ g.DrawBoxOnColumn(documentGridForm, 5, 10, Color.Red);
+ g.DrawBoxOnColumn(documentGridForm, 6, 10, Color.Red);
- g.Flush();
+ g.DrawEllipseOnCell(documentGridForm, 1, 3, Color.Red);
+ g.DrawEllipseOnCell(documentGridForm, 3, 3, Color.Orange);
+ g.DrawEllipseOnCell(documentGridForm, 1, 5, Color.Orange);
+ g.DrawEllipseOnCell(documentGridForm, 3, 5, Color.Orange);
return bmp;
});
diff --git a/pwiz_tools/Skyline/TestTutorial/DiaTutorialTest.cs b/pwiz_tools/Skyline/TestTutorial/DiaTutorialTest.cs
index 961de15385..a16b3566b5 100644
--- a/pwiz_tools/Skyline/TestTutorial/DiaTutorialTest.cs
+++ b/pwiz_tools/Skyline/TestTutorial/DiaTutorialTest.cs
@@ -280,7 +280,7 @@ protected override void DoTest()
allChrom.Left = SkylineWindow.Right + 20;
});
- PauseForScreenShot("Targets view clipped - scrolled left and before fully imported", 26);
+ PauseForTargetsScreenShot("Targets view clipped - scrolled left and before fully imported");
}
WaitForDocumentLoaded(10 * 60 * 1000); // 10 minutes
diff --git a/pwiz_tools/Skyline/TestUtil/PauseAndContinueForm.cs b/pwiz_tools/Skyline/TestUtil/PauseAndContinueForm.cs
index c64b5362ed..7b2fc2e044 100644
--- a/pwiz_tools/Skyline/TestUtil/PauseAndContinueForm.cs
+++ b/pwiz_tools/Skyline/TestUtil/PauseAndContinueForm.cs
@@ -160,6 +160,7 @@ private void EnsurePreviewForm()
private void Continue()
{
+ TestUtilSettings.Default.Save();
Hide();
// Start the tests again
diff --git a/pwiz_tools/Skyline/TestUtil/ScreenshotManager.cs b/pwiz_tools/Skyline/TestUtil/ScreenshotManager.cs
index 99550368b3..47fd2467e6 100644
--- a/pwiz_tools/Skyline/TestUtil/ScreenshotManager.cs
+++ b/pwiz_tools/Skyline/TestUtil/ScreenshotManager.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
@@ -77,8 +76,9 @@ public static Rectangle GetDockedFormBounds(DockableForm ctrl)
public static Rectangle GetDockedFormBoundsInternal(DockableForm dockedForm)
{
var parentRelativeVBounds = dockedForm.Pane.Bounds;
- // The pane bounds do not include the border
- parentRelativeVBounds.Inflate(SystemInformation.BorderSize.Width, SystemInformation.BorderSize.Width);
+ // The pane bounds do not include the border for Document state
+ if (dockedForm.DockState == DockState.Document)
+ parentRelativeVBounds.Inflate(SystemInformation.BorderSize.Width, SystemInformation.BorderSize.Width);
return dockedForm.Pane.Parent.RectangleToScreen(parentRelativeVBounds);
}
@@ -93,9 +93,12 @@ public static Rectangle GetFramedWindowBounds(Control ctrl)
private static Rectangle GetFramedWindowBoundsInternal(Control ctrl)
{
int width = (ctrl as Form)?.DesktopBounds.Width ?? ctrl.Width;
- // The drop shadow is 1/2 the difference between the window width and the client rect width
- // A 3D border width is removed from this and then a standard border width (usually 1) removed
- int dropShadowWidth = (width - ctrl.ClientRectangle.Width) / 2 - SystemInformation.Border3DSize.Width + SystemInformation.BorderSize.Width;
+ // The drop shadow + border are 1/2 the difference between the window width and the client rect width
+ // A border width is removed to keep the border around the window
+ int borderOutsideClient = SystemInformation.BorderSize.Width;
+ if (ctrl is FloatingWindow)
+ borderOutsideClient = 0;
+ int dropShadowWidth = (width - ctrl.ClientRectangle.Width) / 2 - borderOutsideClient;
Size imageSize;
Point sourcePoint;
if (ctrl is Form)
@@ -253,12 +256,14 @@ public string ScreenshotUrl(int screenshotNum)
{
if (string.IsNullOrEmpty(_tutorialSourcePath))
return null;
- return GetTutorialUrl("index.html") + "#s-" + PadScreenshotNum(screenshotNum);
+ return GetTutorialUrl("index.html") + "#s-" + screenshotNum;
}
public string ScreenshotImgUrl(int screenshotNum)
{
- return GetTutorialUrl("s-" + PadScreenshotNum(screenshotNum) + ".png");
+ return GetTutorialUrl("s-" + screenshotNum + ".png");
+ // Use this latter version once the website is updated
+ // return GetTutorialUrl("s-" + PadScreenshotNum(screenshotNum) + ".png");
}
private const string SCREENSHOT_URL_FOLDER = "24-1";
@@ -356,21 +361,37 @@ public void ActivateScreenshotForm(Control screenshotControl)
RunUI(screenshotControl,() => screenshotControl.Focus());
}
- Thread.Sleep(200); // Allow activation message processing on the UI thread
+ Thread.Sleep(500); // Allow activation message processing on the UI thread
- RunUI(screenshotControl, () =>
- {
- var focusText = screenshotControl.GetFocus() as TextBox;
- if (focusText != null)
- {
- focusText.Select(focusText.Text.Length, 0);
- focusText.HideCaret();
- }
- });
+ RunUI(screenshotControl, () => HideSensitiveFocusDisplay(screenshotControl));
Thread.Sleep(10); // Allow selection to repaint on the UI thread
}
+ ///
+ /// Attempt to get more consistent screenshots by hiding sensitive
+ /// display elements indicating a control has the focus, like blinking
+ /// cursors and dotted rectangles on tab controls.
+ ///
+ private void HideSensitiveFocusDisplay(Control screenshotControl)
+ {
+ var focusControl = screenshotControl.GetFocus();
+ if (focusControl is TextBox focusText)
+ {
+ focusText.Select(focusText.Text.Length, 0);
+ focusText.HideCaret();
+ }
+ else if (focusControl is ComboBox { CanSelect: true } focusCombo)
+ {
+ focusCombo.Select(0, 0);
+ focusCombo.HideCaret(); // CONSIDER: This does not seem to work
+ }
+ else if (focusControl is TabControl focusTabControl)
+ {
+ focusTabControl.SelectedTab.Focus();
+ }
+ }
+
private static void RunUI(Control control, Action action)
{
control.Invoke(action);
@@ -383,10 +404,12 @@ public Bitmap TakeShot(Control activeWindow, bool fullScreen = false, string pat
// Check UI and create a blank shot according to the user selection
var newShot = SkylineScreenshot.CreateScreenshot(activeWindow, fullScreen);
- Bitmap shotPic = newShot.Take();
+ var shotPic = newShot.Take();
if (processShot != null)
{
- // execute on window's thread in case delegate accesses UI controls
+ // Processing must include border cleanup if necessary, since
+ // the shot-pic is not guaranteed to need a constant border
+ // Execute processing on window's thread in case delegate accesses UI controls
shotPic = activeWindow.Invoke(processShot, shotPic) as Bitmap;
Assert.IsNotNull(shotPic);
}
@@ -394,7 +417,9 @@ public Bitmap TakeShot(Control activeWindow, bool fullScreen = false, string pat
{
// Tidy up annoying variations in screenshot border due to underlying windows
// Only for unprocessed window screenshots
- CleanupBorder(shotPic);
+ // Floating windows only have a transparent titlebar border
+ var form = activeWindow as DockableForm;
+ shotPic.CleanupBorder(form is { DockState: DockState.Floating });
}
if (scale.HasValue)
@@ -418,52 +443,6 @@ public Bitmap TakeShot(Control activeWindow, bool fullScreen = false, string pat
return shotPic;
}
- private static void CleanupBorder(Bitmap shotPic)
- {
- // Determine border color, then make it consistently that color
- var stats = new Dictionary();
-
- void UpdateStats(int x, int y)
- {
- var c = shotPic.GetPixel(x, y);
- if (stats.ContainsKey(c))
- {
- stats[c]++;
- }
- else
- {
- stats[c] = 1;
- }
- }
-
- for (var x = 0; x < shotPic.Width; x++)
- {
- UpdateStats(x, 0);
- UpdateStats(x, shotPic.Height - 1);
- }
-
- for (var y = 0; y < shotPic.Height; y++)
- {
- UpdateStats(0, y);
- UpdateStats(shotPic.Width - 1, y);
- }
-
- var color = stats.FirstOrDefault(kvp => kvp.Value == stats.Values.Max()).Key;
-
- // Enforce a clean border
- for (var x = 0; x < shotPic.Width; x++)
- {
- shotPic.SetPixel(x, 0, color);
- shotPic.SetPixel(x, shotPic.Height - 1, color);
- }
-
- for (var y = 0; y < shotPic.Height; y++)
- {
- shotPic.SetPixel(0, y, color);
- shotPic.SetPixel(shotPic.Width - 1, y, color);
- }
- }
-
private void SaveToFile(string filePath, Bitmap bmp)
{
if (File.Exists(filePath))
diff --git a/pwiz_tools/Skyline/TestUtil/ScreenshotPreviewForm.Designer.cs b/pwiz_tools/Skyline/TestUtil/ScreenshotPreviewForm.Designer.cs
index 0052f1e848..33ac3846c8 100644
--- a/pwiz_tools/Skyline/TestUtil/ScreenshotPreviewForm.Designer.cs
+++ b/pwiz_tools/Skyline/TestUtil/ScreenshotPreviewForm.Designer.cs
@@ -38,6 +38,7 @@ private void InitializeComponent()
this.buttonImageSource = new System.Windows.Forms.Button();
this.oldScreenshotLabel = new System.Windows.Forms.Label();
this.newScreenshotLabelPanel = new System.Windows.Forms.Panel();
+ this.progressBar = new pwiz.Common.Controls.CustomTextProgressBar();
this.newScreenshotLabel = new System.Windows.Forms.Label();
this.previewFlowLayoutControlPanel = new System.Windows.Forms.FlowLayoutPanel();
this.continueBtn = new System.Windows.Forms.Button();
@@ -64,7 +65,9 @@ private void InitializeComponent()
this.toolStripGotoWeb = new System.Windows.Forms.ToolStripButton();
this.toolStripDescription = new System.Windows.Forms.ToolStripLabel();
this.toolStripSwitchToToolbar = new System.Windows.Forms.ToolStripButton();
- this.progressBar = new pwiz.Common.Controls.CustomTextProgressBar();
+ this.labelOldSize = new System.Windows.Forms.Label();
+ this.labelNewSize = new System.Windows.Forms.Label();
+ this.pictureMatching = new System.Windows.Forms.PictureBox();
((System.ComponentModel.ISupportInitialize)(this.oldScreenshotPictureBox)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.newScreenshotPictureBox)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.previewSplitContainer)).BeginInit();
@@ -79,6 +82,7 @@ private void InitializeComponent()
this.splitBar.Panel2.SuspendLayout();
this.splitBar.SuspendLayout();
this.toolStrip.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.pictureMatching)).BeginInit();
this.SuspendLayout();
//
// oldScreenshotPictureBox
@@ -129,6 +133,8 @@ private void InitializeComponent()
//
this.oldScreenshotLabelPanel.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.oldScreenshotLabelPanel.BackColor = System.Drawing.SystemColors.ActiveBorder;
+ this.oldScreenshotLabelPanel.Controls.Add(this.pictureMatching);
+ this.oldScreenshotLabelPanel.Controls.Add(this.labelOldSize);
this.oldScreenshotLabelPanel.Controls.Add(this.buttonImageSource);
this.oldScreenshotLabelPanel.Controls.Add(this.oldScreenshotLabel);
this.oldScreenshotLabelPanel.Dock = System.Windows.Forms.DockStyle.Top;
@@ -170,6 +176,7 @@ private void InitializeComponent()
//
this.newScreenshotLabelPanel.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.newScreenshotLabelPanel.BackColor = System.Drawing.SystemColors.ActiveCaption;
+ this.newScreenshotLabelPanel.Controls.Add(this.labelNewSize);
this.newScreenshotLabelPanel.Controls.Add(this.progressBar);
this.newScreenshotLabelPanel.Controls.Add(this.newScreenshotLabel);
this.newScreenshotLabelPanel.Dock = System.Windows.Forms.DockStyle.Top;
@@ -179,6 +186,18 @@ private void InitializeComponent()
this.newScreenshotLabelPanel.Size = new System.Drawing.Size(487, 30);
this.newScreenshotLabelPanel.TabIndex = 3;
//
+ // progressBar
+ //
+ this.progressBar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.progressBar.CustomText = "Waiting on Skyline for next screenshot...";
+ this.progressBar.DisplayStyle = pwiz.Common.Controls.ProgressBarDisplayText.CustomText;
+ this.progressBar.Location = new System.Drawing.Point(408, 4);
+ this.progressBar.Name = "progressBar";
+ this.progressBar.Size = new System.Drawing.Size(76, 21);
+ this.progressBar.TabIndex = 1;
+ this.progressBar.Visible = false;
+ //
// newScreenshotLabel
//
this.newScreenshotLabel.BackColor = System.Drawing.Color.Transparent;
@@ -457,17 +476,31 @@ private void InitializeComponent()
this.toolStripSwitchToToolbar.ToolTipText = "Show Text Buttons";
this.toolStripSwitchToToolbar.Click += new System.EventHandler(this.toolStripSwitchToToolbar_Click);
//
- // progressBar
+ // labelOldSize
//
- this.progressBar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
- | System.Windows.Forms.AnchorStyles.Right)));
- this.progressBar.CustomText = "Waiting on Skyline for next screenshot...";
- this.progressBar.DisplayStyle = pwiz.Common.Controls.ProgressBarDisplayText.CustomText;
- this.progressBar.Location = new System.Drawing.Point(408, 4);
- this.progressBar.Name = "progressBar";
- this.progressBar.Size = new System.Drawing.Size(76, 21);
- this.progressBar.TabIndex = 1;
- this.progressBar.Visible = false;
+ this.labelOldSize.AutoSize = true;
+ this.labelOldSize.Location = new System.Drawing.Point(26, 9);
+ this.labelOldSize.Name = "labelOldSize";
+ this.labelOldSize.Size = new System.Drawing.Size(42, 13);
+ this.labelOldSize.TabIndex = 2;
+ this.labelOldSize.Text = "old size";
+ //
+ // labelNewSize
+ //
+ this.labelNewSize.AutoSize = true;
+ this.labelNewSize.Location = new System.Drawing.Point(5, 9);
+ this.labelNewSize.Name = "labelNewSize";
+ this.labelNewSize.Size = new System.Drawing.Size(48, 13);
+ this.labelNewSize.TabIndex = 2;
+ this.labelNewSize.Text = "new size";
+ //
+ // pictureMatching
+ //
+ this.pictureMatching.Location = new System.Drawing.Point(5, 7);
+ this.pictureMatching.Name = "pictureMatching";
+ this.pictureMatching.Size = new System.Drawing.Size(16, 16);
+ this.pictureMatching.TabIndex = 3;
+ this.pictureMatching.TabStop = false;
//
// ScreenshotPreviewForm
//
@@ -490,7 +523,9 @@ private void InitializeComponent()
((System.ComponentModel.ISupportInitialize)(this.previewSplitContainer)).EndInit();
this.previewSplitContainer.ResumeLayout(false);
this.oldScreenshotLabelPanel.ResumeLayout(false);
+ this.oldScreenshotLabelPanel.PerformLayout();
this.newScreenshotLabelPanel.ResumeLayout(false);
+ this.newScreenshotLabelPanel.PerformLayout();
this.previewFlowLayoutControlPanel.ResumeLayout(false);
this.previewFlowLayoutControlPanel.PerformLayout();
this.splitBar.Panel1.ResumeLayout(false);
@@ -501,6 +536,7 @@ private void InitializeComponent()
this.splitBar.ResumeLayout(false);
this.toolStrip.ResumeLayout(false);
this.toolStrip.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.pictureMatching)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
@@ -542,5 +578,8 @@ private void InitializeComponent()
private System.Windows.Forms.Button buttonSwitchToToolStrip;
private System.Windows.Forms.ToolStripButton toolStripSwitchToToolbar;
private System.Windows.Forms.Button buttonImageSource;
+ private System.Windows.Forms.Label labelOldSize;
+ private System.Windows.Forms.Label labelNewSize;
+ private System.Windows.Forms.PictureBox pictureMatching;
}
}
\ No newline at end of file
diff --git a/pwiz_tools/Skyline/TestUtil/ScreenshotPreviewForm.cs b/pwiz_tools/Skyline/TestUtil/ScreenshotPreviewForm.cs
index 6387d5e535..a5e9bd1ffe 100644
--- a/pwiz_tools/Skyline/TestUtil/ScreenshotPreviewForm.cs
+++ b/pwiz_tools/Skyline/TestUtil/ScreenshotPreviewForm.cs
@@ -79,6 +79,7 @@ public partial class ScreenshotPreviewForm : Form
private string _fileLoaded;
private Bitmap _newScreenshot;
private bool _screenshotTaken;
+ private bool? _oldAndNewMatch;
private NextScreenshotProgress _nextScreenshotProgress;
private class NextScreenshotProgress
@@ -121,6 +122,7 @@ public ScreenshotPreviewForm(IPauseTestController pauseTestController, Screensho
// Unfortunately there is not enough information about the image sizes to
// the starting location right here, but this is better than using the Windows default
+ labelOldSize.Text = labelNewSize.Text = string.Empty;
StartPosition = FormStartPosition.Manual;
var savedLocation = TestUtilSettings.Default.PreviewFormLocation;
if (!TestUtilSettings.Default.ManualSizePreview)
@@ -285,9 +287,40 @@ private void UpdatePreviewImages()
lock (_lock)
{
helpTip.SetToolTip(oldScreenshotLabel, _fileLoaded);
- SetPreviewImage(oldScreenshotPictureBox, _oldScreenshot);
+ bool isPlaceholder = string.IsNullOrEmpty(_fileLoaded);
+ SetPreviewSize(labelOldSize, !isPlaceholder ? _oldScreenshot : null);
+ SetPreviewImage(oldScreenshotPictureBox, _oldScreenshot, isPlaceholder);
helpTip.SetToolTip(newScreenshotLabel, _screenshotTaken ? _description : null);
- SetPreviewImage(newScreenshotPictureBox, _newScreenshot);
+ SetPreviewSize(labelNewSize, _screenshotTaken ? _newScreenshot : null);
+ SetPreviewImage(newScreenshotPictureBox, _newScreenshot, !_screenshotTaken);
+ if (!_oldAndNewMatch.HasValue)
+ {
+ pictureMatching.Visible = false;
+ labelOldSize.Left = pictureMatching.Left;
+ }
+ else
+ {
+ pictureMatching.Visible = true;
+ labelOldSize.Left = pictureMatching.Right;
+ var bmpDiff = _oldAndNewMatch.Value
+ ? Skyline.Properties.Resources.Peak
+ : Skyline.Properties.Resources.NoPeak;
+ bmpDiff.MakeTransparent(Color.White);
+ pictureMatching.Image = bmpDiff;
+ }
+ }
+ }
+
+ private static void SetPreviewSize(Label labelSize, Bitmap screenshot)
+ {
+ if (screenshot == null)
+ labelSize.Text = string.Empty;
+ else
+ {
+ lock (screenshot)
+ {
+ labelSize.Text = $@"{screenshot.Width} x {screenshot.Height}px";
+ }
}
}
@@ -513,6 +546,7 @@ private void UpdateScreenshotsAsync(bool showWebImage)
{
Bitmap newScreenshot;
bool shotTaken, waitingForScreenshot;
+ bool? oldAndNewMatch = null;
ScreenshotValues screenshotValues;
lock (_lock)
@@ -528,15 +562,42 @@ private void UpdateScreenshotsAsync(bool showWebImage)
{
newScreenshot = Resources.progress;
}
- else if (!shotTaken)
+ else
{
- newScreenshot = TakeScreenshot(screenshotValues);
- shotTaken = true;
+ if (!shotTaken)
+ {
+ newScreenshot = TakeScreenshot(screenshotValues);
+ shotTaken = true;
+ }
+
+ Bitmap diffImage;
+ lock (oldScreenshot)
+ {
+ lock (newScreenshot)
+ {
+ diffImage = DiffScreenshots(oldScreenshot, newScreenshot);
+ }
+ }
+ if (!ReferenceEquals(diffImage, _oldScreenshot))
+ {
+ oldAndNewMatch = false;
+ }
+ else if (oldScreenshot == null || oldScreenshot.Size != newScreenshot.Size)
+ {
+ oldAndNewMatch = false;
+ }
+ else if (!ReferenceEquals(newScreenshot, Resources.noscreenshot))
+ {
+ oldAndNewMatch = true;
+ }
+ oldScreenshot = diffImage;
}
lock (_lock)
{
_newScreenshot = newScreenshot;
+ _oldScreenshot = oldScreenshot;
+ _oldAndNewMatch = oldAndNewMatch;
_screenshotTaken = shotTaken;
if (shotTaken)
_nextScreenshotProgress = null; // Done waiting for the next screenshot
@@ -546,6 +607,56 @@ private void UpdateScreenshotsAsync(bool showWebImage)
FormStateChangedBackground();
}
+ private Bitmap DiffScreenshots(Bitmap bmpOld, Bitmap bmpNew)
+ {
+ if (bmpNew == null || bmpOld == null || bmpNew.Size != bmpOld.Size)
+ return bmpOld;
+
+ try
+ {
+ return HighlightDifferences(bmpOld, bmpNew, Color.Red);
+ }
+ catch (Exception e)
+ {
+ this.BeginInvoke((Action)(() => PreviewMessageDlg.ShowWithException(this,
+ "Failed to diff bitmaps.", e)));
+ return bmpOld;
+ }
+ }
+
+ public static Bitmap HighlightDifferences(Bitmap bmpOld, Bitmap bmpNew, Color highlightColor, int alpha = 128)
+ {
+ var result = new Bitmap(bmpOld.Width, bmpOld.Height);
+
+ bool diffFound = false;
+ for (int y = 0; y < bmpOld.Height; y++)
+ {
+ for (int x = 0; x < bmpOld.Width; x++)
+ {
+ var pixel1 = bmpOld.GetPixel(x, y);
+ var pixel2 = bmpNew.GetPixel(x, y);
+
+ if (pixel1 != pixel2)
+ {
+ var blendedColor = Color.FromArgb(
+ alpha,
+ highlightColor.R * alpha / 255 + pixel1.R * (255 - alpha) / 255,
+ highlightColor.G * alpha / 255 + pixel1.G * (255 - alpha) / 255,
+ highlightColor.B * alpha / 255 + pixel1.B * (255 - alpha) / 255
+ );
+ result.SetPixel(x, y, blendedColor);
+ diffFound = true;
+ }
+ else
+ {
+ result.SetPixel(x, y, pixel1);
+ }
+ }
+ }
+
+ return diffFound ? result : bmpOld;
+ }
+
private struct ScreenshotValues
{
public static readonly ScreenshotValues Empty = new ScreenshotValues(null, false, null);
@@ -610,15 +721,19 @@ private Bitmap LoadScreenshot(string file, string uri)
}
}
- private void SetPreviewImage(PictureBox previewBox, Bitmap screenshot)
+ private void SetPreviewImage(PictureBox previewBox, Bitmap screenshot, bool isPlaceHolder)
{
var newImage = screenshot;
- if (screenshot != null)
+ if (screenshot != null && !isPlaceHolder)
{
- var containerSize = !autoSizeWindowCheckbox.Checked ? previewBox.Size : Size.Empty;
- var screenshotSize = CalcBitmapSize(screenshot, containerSize);
- if (screenshotSize != screenshot.Size)
+ lock (screenshot)
+ {
+ var containerSize = !autoSizeWindowCheckbox.Checked ? previewBox.Size : Size.Empty;
+ var screenshotSize = CalcBitmapSize(screenshot, containerSize);
+ // Always make a copy to avoid having PictureBox lock the bitmap
+ // which can cause issues with future image diffs
newImage = new Bitmap(screenshot, screenshotSize);
+ }
}
previewBox.Image = newImage;
@@ -847,7 +962,7 @@ private void ContinueInternal()
{
Hide();
}
- else if(File.Exists(_screenshotManager.ScreenshotSourceFile(_screenshotNum)))
+ else if (File.Exists(_screenshotManager.ScreenshotSourceFile(_screenshotNum)))
{
FormStateChanged();
}
diff --git a/pwiz_tools/Skyline/TestUtil/ScreenshotProcessingExtensions.cs b/pwiz_tools/Skyline/TestUtil/ScreenshotProcessingExtensions.cs
new file mode 100644
index 0000000000..d4e8422a07
--- /dev/null
+++ b/pwiz_tools/Skyline/TestUtil/ScreenshotProcessingExtensions.cs
@@ -0,0 +1,126 @@
+/*
+ * Original author: Brendan MacLean ,
+ * MacCoss Lab, Department of Genome Sciences, UW
+ *
+ * Copyright 2014 University of Washington - Seattle, WA
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using pwiz.Skyline.Controls.Databinding;
+
+namespace pwiz.SkylineTestUtil
+{
+ ///
+ /// A collection of extensions to the class in support
+ /// of screenshot processing for tutorial screenshots.
+ ///
+ public static class ScreenshotProcessingExtensions
+ {
+ ///
+ /// The most common color #FF707070 for a window border on Windows 10 with a white background.
+ /// On a red background this becomes #FF68251F
+ ///
+ private static readonly Color STANDARD_BORDER_COLOR = Color.FromArgb(0x70, 0x70, 0x70);
+
+ public static Bitmap CleanupBorder(this Bitmap bmp, bool titleBarOnly = false)
+ {
+ // Floating dockable forms have only a transparent border at the top
+ if (titleBarOnly)
+ return bmp.CleanupBorder(new Rectangle(0, 0, bmp.Width, 1));
+ else
+ return bmp.CleanupBorder(new Rectangle(0, 0, bmp.Width, bmp.Height));
+ }
+
+ public static Bitmap CleanupBorder(this Bitmap bmp, Rectangle rectWindow)
+ {
+ return bmp.CleanupBorder(STANDARD_BORDER_COLOR, rectWindow);
+ }
+
+ private static Bitmap CleanupBorder(this Bitmap bmp, Color? color, Rectangle rect)
+ {
+ var colorCounts = new Dictionary();
+ foreach (var point in RectPoints(rect))
+ AddPixel(point, bmp, colorCounts);
+ var maxColorCount = colorCounts.Values.Max();
+ var bestBorderColor = colorCounts.FirstOrDefault(kvp => kvp.Value == maxColorCount).Key;
+
+ // If no color is specified, use the most common color in the border.
+ // This is dependent on the screen background color. So, it should not
+ // be used in general, but only to figure out the best color to make
+ // the standard for all saved screenshots. Currently: #FF707070
+ // Also, use the best color if it is white as it is for stand-alone graphs
+ if (!color.HasValue || bestBorderColor.ToArgb() == Color.White.ToArgb())
+ {
+ color = bestBorderColor;
+ }
+
+ foreach (var point in RectPoints(rect))
+ bmp.SetPixel(point.X, point.Y, color.Value);
+ return bmp;
+ }
+
+ private static void AddPixel(Point point, Bitmap shotPic, IDictionary colorCounts)
+ {
+ var c = shotPic.GetPixel(point.X, point.Y);
+ if (!colorCounts.ContainsKey(c))
+ colorCounts.Add(c, 0);
+ colorCounts[c]++;
+ }
+
+ private static IEnumerable RectPoints(Rectangle rect)
+ {
+ for (var x = 0; x < rect.Width; x++)
+ {
+ yield return new Point(rect.X + x, rect.Y); // upper edge
+ yield return new Point(rect.X + x, rect.Y + rect.Height - 1); // lower edge
+ }
+
+ for (var y = 0; y < rect.Height; y++)
+ {
+ yield return new Point(rect.X, rect.Y + y); // left edge
+ yield return new Point(rect.X + rect.Width - 1, rect.Y + y); // right edge
+ }
+ }
+
+ public static void DrawBoxOnColumn(this Graphics g, DocumentGridForm documentGridForm, int column, int rows, Color color, int lineWidth = 3)
+ {
+ var rect = documentGridForm.DataGridView.GetCellDisplayRectangle(column, 0, true); // column's top data cell
+ rect.Y += GetGridViewYOffset(documentGridForm);
+ rect.Height *= rows; // draw rectangle around all rows
+ g.DrawRectangle(new Pen(color, lineWidth), rect);
+ }
+
+ public static void DrawEllipseOnCell(this Graphics g, DocumentGridForm documentGridForm, int row, int column, Color color, int lineWidth = 3)
+ {
+ var dataGridView = documentGridForm.DataGridView;
+ var text = dataGridView.Rows[row].Cells[column].FormattedValue?.ToString();
+ var stringSize = g.MeasureString(text, dataGridView.Font);
+
+ var rect = dataGridView.GetCellDisplayRectangle(column, row, true);
+ rect.Y += GetGridViewYOffset(documentGridForm);
+ rect.Width = Convert.ToInt16(stringSize.Width * 1.1); // scale-up ellipse size so shape isn't too tight around text
+
+ g.DrawEllipse(new Pen(color, lineWidth), rect);
+ }
+ private static int GetGridViewYOffset(DocumentGridForm documentGridForm)
+ {
+ // compute top-left corner of data grid's cells, 4px offset puts shapes in the correct place
+ return documentGridForm.NavBar.Height + documentGridForm.DataGridView.ColumnHeadersHeight - 4;
+ }
+ }
+}
diff --git a/pwiz_tools/Skyline/TestUtil/TestFunctional.cs b/pwiz_tools/Skyline/TestUtil/TestFunctional.cs
index 1e6e60c1de..74d56eedfd 100644
--- a/pwiz_tools/Skyline/TestUtil/TestFunctional.cs
+++ b/pwiz_tools/Skyline/TestUtil/TestFunctional.cs
@@ -286,18 +286,32 @@ protected static TDlg ShowDialog(Action act, int millis = -1) where TDlg :
dlg = WaitForOpenForm(millis);
Assert.IsNotNull(dlg);
+ return dlg;
+ }
+
+ private static void EnsureScreenshotIcon(Form dlg)
+ {
// Making sure if the form has a visible icon it's Skyline release icon, not daily one.
if (IsRecordingScreenShots && dlg.ShowIcon && !ReferenceEquals(dlg, SkylineWindow))
{
if (dlg.FormBorderStyle != FormBorderStyle.FixedDialog ||
- dlg.Icon.Handle == Resources.Skyline.Handle) // Normally a fixed dialog will not have the Skyline icon handle
+ AreIconsEqual(dlg.Icon, Resources.Skyline)) // Normally a fixed dialog will not have the Skyline icon
{
- var ico = dlg.Icon.Handle;
- if (ico != SkylineWindow.Icon.Handle)
+ if (!AreIconsEqual(dlg.Icon, SkylineWindow.Icon))
RunUI(() => dlg.Icon = SkylineWindow.Icon);
}
}
- return dlg;
+ }
+
+ private static bool AreIconsEqual(Icon icon1, Icon icon2)
+ {
+ using var ms1 = new MemoryStream();
+ using var ms2 = new MemoryStream();
+
+ icon1.Save(ms1);
+ icon2.Save(ms2);
+
+ return ms1.ToArray().SequenceEqual(ms2.ToArray());
}
///
@@ -791,6 +805,8 @@ public static Form TryWaitForOpenForm(Type formType, int millis = WAIT_TIME, Fun
PauseAndContinueForm.Show(string.Format("Pausing for {0}", formType));
}
+ EnsureScreenshotIcon(tForm);
+
return tForm;
}
@@ -1467,7 +1483,7 @@ private static int IndexOfItem(ToolStripItemCollection items, string itemText)
protected Bitmap ClipSkylineWindowShotWithForms(Bitmap skylineWindowBmp, IList dockableForms)
{
- return ClipBitmap(skylineWindowBmp, ComputeDockedFormsUnionRectangle(dockableForms));
+ return ClipBitmap(skylineWindowBmp.CleanupBorder(), ComputeDockedFormsUnionRectangle(dockableForms));
}
private Rectangle ComputeDockedFormsUnionRectangle(IList dockableForms)
@@ -1490,7 +1506,7 @@ protected Bitmap ClipSelectionStatus(Bitmap skylineWindowBmp)
int clipWidth = SkylineWindow.StatusSelectionWidth;
int clipHeight = SkylineWindow.StatusBarHeight + 1;
var cropRect = new Rectangle(skylineWindowBmp.Width - clipWidth, skylineWindowBmp.Height - clipHeight, clipWidth, clipHeight);
- return ClipBitmap(skylineWindowBmp, cropRect);
+ return ClipBitmap(skylineWindowBmp.CleanupBorder(), cropRect);
}
protected Bitmap ClipGridToolbarSelection(Bitmap documentGridBmp)
@@ -1911,7 +1927,7 @@ private void PauseForScreenShotInternal(string description, Type formType = null
if (IsAutoScreenShotMode)
{
- // Thread.Sleep(500); // Wait for UI to settle down - Necessary?
+ Thread.Sleep(500); // Wait for UI to settle down - or screenshots can end up blurry
_shotManager.ActivateScreenshotForm(screenshotForm);
var fileToSave = _shotManager.ScreenshotDestFile(ScreenshotCounter);
_shotManager.TakeShot(screenshotForm, fullScreen, fileToSave, processShot);
diff --git a/pwiz_tools/Skyline/TestUtil/TestUtil.csproj b/pwiz_tools/Skyline/TestUtil/TestUtil.csproj
index 9509fa8b4d..80dead17ce 100644
--- a/pwiz_tools/Skyline/TestUtil/TestUtil.csproj
+++ b/pwiz_tools/Skyline/TestUtil/TestUtil.csproj
@@ -185,6 +185,7 @@
+