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 @@ +