diff --git a/MatterControl.Printing/Settings/SliceSettingsFields.cs b/MatterControl.Printing/Settings/SliceSettingsFields.cs index 953cb4db7..70dea39d5 100644 --- a/MatterControl.Printing/Settings/SliceSettingsFields.cs +++ b/MatterControl.Printing/Settings/SliceSettingsFields.cs @@ -559,15 +559,6 @@ namespace MatterHackers.MatterControl.SlicerConfiguration Converter = new AsPercentOrDirectFirst(), }, new SliceSettingData() - { - SlicerConfigName = SettingsKey.fill_pattern, - PresentationName = "Fill Pattern".Localize(), - HelpText = "The geometric shape of the support structure for the inside of parts.".Localize(), - DataEditType = DataEditTypes.LIST, - ListValues = "rectilinear,line,grid,concentric,honeycomb,hilbertcurve,achimedeancords,octagramspiral,3dhoneycomb", - DefaultValue = "honeycomb" - }, - new SliceSettingData() { SlicerConfigName = SettingsKey.fill_thin_gaps, PresentationName = "Fill Thin Gaps".Localize(), @@ -893,7 +884,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration HelpText = "The geometric shape of the support structure for the inside of parts.".Localize(), DataEditType = DataEditTypes.LIST, ShowIfSet = "!sla_printer", - ListValues = "GRID,TRIANGLES,HEXAGON,LINES,CONCENTRIC", + ListValues = "GRID,TRIANGLES,HEXAGON,GYROID,LINES,CONCENTRIC", DefaultValue = "TRIANGLES", Converter = new ValueConverter(), }, @@ -1657,15 +1648,6 @@ namespace MatterHackers.MatterControl.SlicerConfiguration Converter = new AsPercentOfReferenceOrDirect(SettingsKey.perimeter_speed) }, new SliceSettingData() - { - SlicerConfigName = SettingsKey.solid_fill_pattern, - PresentationName = "Top/Bottom Fill Pattern".Localize(), - HelpText = "The pattern used on the bottom and top layers of the print.".Localize(), - DataEditType = DataEditTypes.LIST, - ListValues = "rectilinear,concentric,hilbertcurve,achimedeancords,octagramspiral", - DefaultValue = "rectilinear" - }, - new SliceSettingData() { SlicerConfigName = SettingsKey.solid_infill_extrusion_width, PresentationName = "Solid Infill".Localize(), diff --git a/MatterControlLib/ApplicationView/ApplicationController.cs b/MatterControlLib/ApplicationView/ApplicationController.cs index 6b49d7489..e55e793ca 100644 --- a/MatterControlLib/ApplicationView/ApplicationController.cs +++ b/MatterControlLib/ApplicationView/ApplicationController.cs @@ -2933,12 +2933,16 @@ namespace MatterHackers.MatterControl string settingsFilePath = ProfileManager.Instance.ProfilePath(printer.Settings.ID); - using (var file = File.OpenWrite(archivePath)) - using (var zip = new ZipArchive(file, ZipArchiveMode.Create)) + // if the printer was deleted while printing the path can be null + if (settingsFilePath != null) { - zip.CreateEntryFromFile(sourcePath, "PrinterPlate.mcx"); - zip.CreateEntryFromFile(settingsFilePath, printer.Settings.GetValue(SettingsKey.printer_name) + ".printer"); - zip.CreateEntryFromFile(gcodeFilePath, "sliced.gcode"); + using (var file = File.OpenWrite(archivePath)) + using (var zip = new ZipArchive(file, ZipArchiveMode.Create)) + { + zip.CreateEntryFromFile(sourcePath, "PrinterPlate.mcx"); + zip.CreateEntryFromFile(settingsFilePath, printer.Settings.GetValue(SettingsKey.printer_name) + ".printer"); + zip.CreateEntryFromFile(gcodeFilePath, "sliced.gcode"); + } } } diff --git a/MatterControlLib/ConfigurationPage/ApplicationSettings/ThemeColorPanel.cs b/MatterControlLib/ConfigurationPage/ApplicationSettings/ThemeColorPanel.cs index 3dc467f4e..92e9bb9e6 100644 --- a/MatterControlLib/ConfigurationPage/ApplicationSettings/ThemeColorPanel.cs +++ b/MatterControlLib/ConfigurationPage/ApplicationSettings/ThemeColorPanel.cs @@ -147,8 +147,8 @@ namespace MatterHackers.MatterControl.ConfigurationPage { HAnchor = HAnchor.Absolute, VAnchor = VAnchor.Absolute, - Width = 80, - Height = 65, + Width = 80 * GuiWidget.DeviceScale, + Height = 65 * GuiWidget.DeviceScale, Mode = themeName, Border = 1, BorderColor = theme.BorderColor20, @@ -169,13 +169,14 @@ namespace MatterHackers.MatterControl.ConfigurationPage if (themeName == AppContext.ThemeSet.ThemesetID) { - var imageBuffer = new ImageBuffer(35, 35); + var imageSize = (int)(35 * GuiWidget.DeviceScale); + var imageBuffer = new ImageBuffer(imageSize, imageSize); var graphics = imageBuffer.NewGraphics2D(); previewContainer.BorderColor = AppContext.Theme.AccentMimimalOverlay; previewContainer.Border = 1; - var arrowHeight = 35; + var arrowHeight = 35 * GuiWidget.DeviceScale; var upArrow = new VertexStorage(); upArrow.MoveTo(0, 0); @@ -183,8 +184,8 @@ namespace MatterHackers.MatterControl.ConfigurationPage upArrow.LineTo(0, -arrowHeight); upArrow.LineTo(0, 0); - graphics.Render(upArrow, new Vector2(0, 35), AppContext.Theme.PrimaryAccentColor); - graphics.Render(this.CheckMark, 4, 17); + graphics.Render(upArrow, new Vector2(0, 35 * GuiWidget.DeviceScale), AppContext.Theme.PrimaryAccentColor); + graphics.Render(this.CheckMark, 4 * GuiWidget.DeviceScale, 17 * GuiWidget.DeviceScale); imageBuffer.SetPreMultiply(); diff --git a/MatterControlLib/ConfigurationPage/ApplicationSettings/ThemePreviewButton.cs b/MatterControlLib/ConfigurationPage/ApplicationSettings/ThemePreviewButton.cs index f0d8465a7..6a8cf5d8e 100644 --- a/MatterControlLib/ConfigurationPage/ApplicationSettings/ThemePreviewButton.cs +++ b/MatterControlLib/ConfigurationPage/ApplicationSettings/ThemePreviewButton.cs @@ -58,7 +58,7 @@ namespace MatterHackers.MatterControl.ConfigurationPage HAnchor = HAnchor.Absolute | HAnchor.Left, VAnchor = VAnchor.Stretch, Margin = new BorderDouble(0), - Width = 20, + Width = 20 * GuiWidget.DeviceScale, BackgroundColor = theme.MinimalShade, }; this.AddChild(secondaryBackground); @@ -67,7 +67,7 @@ namespace MatterHackers.MatterControl.ConfigurationPage { HAnchor = HAnchor.Stretch, VAnchor = VAnchor.Absolute | VAnchor.Top, - Height = 6, + Height = 6 * GuiWidget.DeviceScale, Margin = new BorderDouble(left: 25), BackgroundColor = primaryAccentColor, }; @@ -77,8 +77,8 @@ namespace MatterHackers.MatterControl.ConfigurationPage { HAnchor = HAnchor.Absolute | HAnchor.Left, VAnchor = VAnchor.Absolute | VAnchor.Top, - Height = 8, - Width = 8, + Height = 8 * GuiWidget.DeviceScale, + Width = 8 * GuiWidget.DeviceScale, Margin = new BorderDouble(left: 6, top: 6), BackgroundColor = primaryAccentColor, }; @@ -88,8 +88,8 @@ namespace MatterHackers.MatterControl.ConfigurationPage { HAnchor = HAnchor.Absolute | HAnchor.Left, VAnchor = VAnchor.Absolute | VAnchor.Top, - Height = 8, - Width = 8, + Height = 8 * GuiWidget.DeviceScale, + Width = 8 * GuiWidget.DeviceScale, Margin = new BorderDouble(left: 6, top: 20), BackgroundColor = primaryAccentColor, }; @@ -99,8 +99,8 @@ namespace MatterHackers.MatterControl.ConfigurationPage { HAnchor = HAnchor.Absolute | HAnchor.Left, VAnchor = VAnchor.Absolute | VAnchor.Top, - Height = 8, - Width = 8, + Height = 8 * GuiWidget.DeviceScale, + Width = 8 * GuiWidget.DeviceScale, Margin = new BorderDouble(left: 6, top: 34), BackgroundColor = primaryAccentColor, }; @@ -110,7 +110,7 @@ namespace MatterHackers.MatterControl.ConfigurationPage { HAnchor = HAnchor.Stretch, VAnchor = VAnchor.Absolute | VAnchor.Top, - Height = 37, + Height = 37 * GuiWidget.DeviceScale, Margin = new BorderDouble(left: 25, top: 12), BackgroundColor = theme.SlightShade, }; diff --git a/MatterControlLib/CustomWidgets/InlineStringEdit.cs b/MatterControlLib/CustomWidgets/InlineStringEdit.cs index 43672eae5..4fdab7b6e 100644 --- a/MatterControlLib/CustomWidgets/InlineStringEdit.cs +++ b/MatterControlLib/CustomWidgets/InlineStringEdit.cs @@ -47,9 +47,14 @@ namespace MatterHackers.MatterControl.CustomWidgets protected FlowLayoutWidget rightPanel; private GuiWidget editButton; private GuiWidget saveButton; - private SearchInputBox searchPanel; + private TextEditWithInlineCancel textEditWithInlineCancel; - public InlineStringEdit(string stringValue, ThemeConfig theme, string automationName, bool boldFont = false, bool editable = true) + public InlineStringEdit(string stringValue, + ThemeConfig theme, + string automationName, + bool boldFont = false, + bool editable = true, + string emptyText = null) : base(theme) { this.Padding = theme.ToolbarPadding; @@ -75,27 +80,27 @@ namespace MatterHackers.MatterControl.CustomWidgets Name = automationName + " Save", }; - searchPanel = new SearchInputBox(theme) + textEditWithInlineCancel = new TextEditWithInlineCancel(theme, emptyText) { Visible = false, Margin = new BorderDouble(left: 4) }; - searchPanel.searchInput.ActualTextEditWidget.EnterPressed += (s, e) => + textEditWithInlineCancel.TextEditWidget.ActualTextEditWidget.EnterPressed += (s, e) => { - this.Text = searchPanel.Text; + this.Text = textEditWithInlineCancel.Text; this.SetVisibility(showEditPanel: false); this.ValueChanged?.Invoke(this, null); }; - searchPanel.searchInput.Name = automationName + " Field"; + textEditWithInlineCancel.TextEditWidget.Name = automationName + " Field"; - searchPanel.ResetButton.Name = "Close Title Edit"; - searchPanel.ResetButton.ToolTipText = "Close".Localize(); - searchPanel.ResetButton.Click += (s, e) => + textEditWithInlineCancel.ResetButton.Name = "Close Title Edit"; + textEditWithInlineCancel.ResetButton.ToolTipText = "Close".Localize(); + textEditWithInlineCancel.ResetButton.Click += (s, e) => { this.SetVisibility(showEditPanel: false); }; - this.AddChild(searchPanel); + this.AddChild(textEditWithInlineCancel); rightPanel = new FlowLayoutWidget(); @@ -115,7 +120,7 @@ namespace MatterHackers.MatterControl.CustomWidgets } else { - searchPanel.Text = this.Text; + textEditWithInlineCancel.Text = this.Text; this.SetVisibility(showEditPanel: true); } }; @@ -123,7 +128,11 @@ namespace MatterHackers.MatterControl.CustomWidgets saveButton.Click += (s, e) => { - this.Text = searchPanel.Text; + if (!string.IsNullOrEmpty(textEditWithInlineCancel.Text)) + { + this.Text = textEditWithInlineCancel.Text; + } + this.SetVisibility(showEditPanel: false); }; rightPanel.AddChild(saveButton); @@ -137,7 +146,18 @@ namespace MatterHackers.MatterControl.CustomWidgets public override string Text { - get => titleText.Text; + get + { + // if the user is still editing the text (has not canceled) + // and there is some text there, return the work in progress. + if (textEditWithInlineCancel.Visible && !string.IsNullOrEmpty(textEditWithInlineCancel.Text)) + { + return textEditWithInlineCancel.Text; + } + + return titleText.Text; + } + set { if (titleText.Text != value) @@ -154,7 +174,7 @@ namespace MatterHackers.MatterControl.CustomWidgets titleText.Visible = !showEditPanel; saveButton.Visible = showEditPanel; - searchPanel.Visible = showEditPanel; + textEditWithInlineCancel.Visible = showEditPanel; } } } \ No newline at end of file diff --git a/MatterControlLib/DataStorage/Models.cs b/MatterControlLib/DataStorage/Models.cs index f39228fb7..420732283 100644 --- a/MatterControlLib/DataStorage/Models.cs +++ b/MatterControlLib/DataStorage/Models.cs @@ -325,6 +325,8 @@ namespace MatterHackers.MatterControl.DataStorage public bool PrintComplete { get; set; } + public bool PrintCanceled { get; set; } + public DateTime PrintEnd { get; set; } [Indexed] diff --git a/MatterControlLib/DesignTools/Operations/Image/LinearExtrudeObject3D.cs b/MatterControlLib/DesignTools/Operations/Image/LinearExtrudeObject3D.cs index a1eff68c3..3426e3823 100644 --- a/MatterControlLib/DesignTools/Operations/Image/LinearExtrudeObject3D.cs +++ b/MatterControlLib/DesignTools/Operations/Image/LinearExtrudeObject3D.cs @@ -66,21 +66,28 @@ namespace MatterHackers.MatterControl.DesignTools public override void Flatten(UndoBuffer undoBuffer) { - // only keep the mesh and get rid of everything else - using (RebuildLock()) + if (Mesh == null) { - var meshOnlyItem = new Object3D() - { - Mesh = this.Mesh.Copy(CancellationToken.None) - }; - - meshOnlyItem.CopyProperties(this, Object3DPropertyFlags.All); - - // and replace us with the children - undoBuffer.AddAndDo(new ReplaceCommand(new[] { this }, new[] { meshOnlyItem })); + Remove(undoBuffer); } + else + { + // only keep the mesh and get rid of everything else + using (RebuildLock()) + { + var meshOnlyItem = new Object3D() + { + Mesh = this.Mesh.Copy(CancellationToken.None) + }; - Invalidate(InvalidateType.Children); + meshOnlyItem.CopyProperties(this, Object3DPropertyFlags.All); + + // and replace us with the children + undoBuffer.AddAndDo(new ReplaceCommand(new[] { this }, new[] { meshOnlyItem })); + } + + Invalidate(InvalidateType.Children); + } } public LinearExtrudeObject3D() diff --git a/MatterControlLib/DesignTools/Primitives/ImageObject3D.cs b/MatterControlLib/DesignTools/Primitives/ImageObject3D.cs index 0b37280cc..37375884e 100644 --- a/MatterControlLib/DesignTools/Primitives/ImageObject3D.cs +++ b/MatterControlLib/DesignTools/Primitives/ImageObject3D.cs @@ -77,29 +77,32 @@ namespace MatterHackers.MatterControl.DesignTools { if (_image == null) { - _image = this.LoadImage(); + // set a temp image so we don't have any problems with threading + var image = this.LoadImage(); - if (_image != null) + if (image != null) { if (this.Invert) { - _image = InvertLightness.DoInvertLightness(_image); + image = InvertLightness.DoInvertLightness(image); } } else // bad load { - _image = new ImageBuffer(200, 200); - var graphics2D = _image.NewGraphics2D(); + image = new ImageBuffer(200, 100); + var graphics2D = image.NewGraphics2D(); graphics2D.Clear(Color.White); - graphics2D.DrawString("Bad Load", 100, 100); + graphics2D.DrawString("Image Missing".Localize(), image.Width / 2, image.Height / 2, 20, Agg.Font.Justification.Center, Agg.Font.Baseline.BoundsCenter); } // we don't want to invalidate on the mesh change using (RebuildLock()) { - base.Mesh = this.InitMesh() ?? PlatonicSolids.CreateCube(100, 100, 0.2); + base.Mesh = this.InitMesh(image) ?? PlatonicSolids.CreateCube(100, 100, 0.2); } + _image = image; + // send the invalidate on image change Parent?.Invalidate(new InvalidateArgs(this, InvalidateType.Image)); } @@ -162,7 +165,7 @@ namespace MatterHackers.MatterControl.DesignTools using (this.RebuildLock()) { // TODO: Revise fallback mesh - base.Mesh = this.InitMesh() ?? PlatonicSolids.CreateCube(100, 100, 0.2); + base.Mesh = this.InitMesh(this.Image) ?? PlatonicSolids.CreateCube(100, 100, 0.2); } } @@ -174,16 +177,15 @@ namespace MatterHackers.MatterControl.DesignTools public override Task Rebuild() { - InitMesh(); + InitMesh(this.Image); return base.Rebuild(); } - private Mesh InitMesh() + private Mesh InitMesh(ImageBuffer imageBuffer) { if (!string.IsNullOrWhiteSpace(this.AssetPath)) { - var imageBuffer = this.Image; if (imageBuffer != null) { ScaleMmPerPixels = Math.Min(DefaultSizeMm / imageBuffer.Width, DefaultSizeMm / imageBuffer.Height); diff --git a/MatterControlLib/History/PrintHistoryListItem.cs b/MatterControlLib/History/PrintHistoryListItem.cs index a64023b3c..33df63140 100644 --- a/MatterControlLib/History/PrintHistoryListItem.cs +++ b/MatterControlLib/History/PrintHistoryListItem.cs @@ -147,6 +147,11 @@ namespace MatterHackers.MatterControl.PrintHistory } } + if (printTask.PrintCanceled) + { + timeIndicator.Text += " - Canceled"; + } + timeIndicator.Margin = new BorderDouble(right: 6); timeIndicator.TextColor = timeTextColor; @@ -196,10 +201,14 @@ namespace MatterHackers.MatterControl.PrintHistory indicator.BackgroundColor = new Color(38, 147, 51, 180); } } - else + else if (printTask.PrintCanceled) { indicator.BackgroundColor = new Color(252, 209, 22, 180); } + else + { + indicator.BackgroundColor = Color.LightGray; + } } private string GetPrintInfo() @@ -420,6 +429,8 @@ namespace MatterHackers.MatterControl.PrintHistory public bool Compleated { get; set; } + public bool Canceled { get; internal set; } + public double RecoveryCount { get; set; } public string ItemsPrinted { get; set; } @@ -447,6 +458,7 @@ namespace MatterHackers.MatterControl.PrintHistory Start = printTask.PrintStart, End = printTask.PrintEnd, Compleated = printTask.PrintComplete, + Canceled = printTask.PrintCanceled, PrintQuality = printTask.PrintQuality, ItemsPrinted = groupNames, Minutes = printTask.PrintTimeMinutes, diff --git a/MatterControlLib/Library/Widgets/LibraryWidget.cs b/MatterControlLib/Library/Widgets/LibraryWidget.cs index 8e5d99e5d..e92762b2a 100644 --- a/MatterControlLib/Library/Widgets/LibraryWidget.cs +++ b/MatterControlLib/Library/Widgets/LibraryWidget.cs @@ -98,12 +98,12 @@ namespace MatterHackers.MatterControl.PrintLibrary breadCrumbWidget = new FolderBreadCrumbWidget(libraryContext, theme); navBar.AddChild(breadCrumbWidget); - var searchPanel = new SearchInputBox(theme) + var searchPanel = new TextEditWithInlineCancel(theme) { Visible = false, Margin = new BorderDouble(10, 0, 5, 0), }; - searchPanel.searchInput.ActualTextEditWidget.EnterPressed += (s, e) => + searchPanel.TextEditWidget.ActualTextEditWidget.EnterPressed += (s, e) => { this.PerformSearch(); }; @@ -112,13 +112,13 @@ namespace MatterHackers.MatterControl.PrintLibrary breadCrumbWidget.Visible = true; searchPanel.Visible = false; - searchPanel.searchInput.Text = ""; + searchPanel.TextEditWidget.Text = ""; this.ClearSearch(); }; // Store a reference to the input field - this.searchInput = searchPanel.searchInput; + this.searchInput = searchPanel.TextEditWidget; navBar.AddChild(searchPanel); diff --git a/MatterControlLib/Library/Widgets/PrintLibraryWidget.cs b/MatterControlLib/Library/Widgets/PrintLibraryWidget.cs index 002504cdb..7613d4af8 100644 --- a/MatterControlLib/Library/Widgets/PrintLibraryWidget.cs +++ b/MatterControlLib/Library/Widgets/PrintLibraryWidget.cs @@ -251,12 +251,12 @@ namespace MatterHackers.MatterControl.PrintLibrary breadCrumbWidget = new FolderBreadCrumbWidget(workspace.LibraryView, theme); navBar.AddChild(breadCrumbWidget); - var searchPanel = new SearchInputBox(theme) + var searchPanel = new TextEditWithInlineCancel(theme) { Visible = false, Margin = new BorderDouble(10, 0, 5, 0), }; - searchPanel.searchInput.ActualTextEditWidget.EnterPressed += (s, e) => + searchPanel.TextEditWidget.ActualTextEditWidget.EnterPressed += (s, e) => { this.PerformSearch(); }; @@ -265,13 +265,13 @@ namespace MatterHackers.MatterControl.PrintLibrary breadCrumbWidget.Visible = true; searchPanel.Visible = false; - searchPanel.searchInput.Text = ""; + searchPanel.TextEditWidget.Text = ""; this.ClearSearch(); }; // Store a reference to the input field - this.searchInput = searchPanel.searchInput; + this.searchInput = searchPanel.TextEditWidget; navBar.AddChild(searchPanel); @@ -537,40 +537,42 @@ namespace MatterHackers.MatterControl.PrintLibrary } } - public class SearchInputBox : GuiWidget + public class TextEditWithInlineCancel : GuiWidget { - internal MHTextEditWidget searchInput; + public MHTextEditWidget TextEditWidget { get; } public GuiWidget ResetButton { get; } - public SearchInputBox(ThemeConfig theme, string emptyText = null) + public TextEditWithInlineCancel(ThemeConfig theme, string emptyText = null) { + if (emptyText == null) + { + emptyText = "Search".Localize(); + } + this.VAnchor = VAnchor.Center | VAnchor.Fit; this.HAnchor = HAnchor.Stretch; - searchInput = new MHTextEditWidget("", theme, messageWhenEmptyAndNotSelected: emptyText ?? "Search".Localize()) + TextEditWidget = new MHTextEditWidget("", theme, messageWhenEmptyAndNotSelected: emptyText) { - Name = "Search Library Edit", HAnchor = HAnchor.Stretch, VAnchor = VAnchor.Center }; - this.AddChild(searchInput); + this.AddChild(TextEditWidget); - var resetButton = theme.CreateSmallResetButton(); - resetButton.HAnchor |= HAnchor.Right; - resetButton.VAnchor |= VAnchor.Center; - resetButton.Name = "Close Search"; - resetButton.ToolTipText = "Clear".Localize(); + this.ResetButton = theme.CreateSmallResetButton(); + ResetButton.HAnchor |= HAnchor.Right; + ResetButton.VAnchor |= VAnchor.Center; + ResetButton.Name = "Close Search"; + ResetButton.ToolTipText = "Clear".Localize(); - this.AddChild(resetButton); - - this.ResetButton = resetButton; + this.AddChild(ResetButton); } public override string Text { - get => searchInput.ActualTextEditWidget.Text; - set => searchInput.ActualTextEditWidget.Text = value; + get => TextEditWidget.ActualTextEditWidget.Text; + set => TextEditWidget.ActualTextEditWidget.Text = value; } } } diff --git a/MatterControlLib/Library/Widgets/SearchableTreePanel.cs b/MatterControlLib/Library/Widgets/SearchableTreePanel.cs index d52bdaf62..3f0901793 100644 --- a/MatterControlLib/Library/Widgets/SearchableTreePanel.cs +++ b/MatterControlLib/Library/Widgets/SearchableTreePanel.cs @@ -41,7 +41,7 @@ namespace MatterHackers.MatterControl.PrintLibrary { public abstract class SearchableTreePanel : FlowLayoutWidget { - protected SearchInputBox searchBox; + protected TextEditWithInlineCancel searchBox; protected TreeView treeView; protected Splitter horizontalSplitter; protected ThemeConfig theme; @@ -55,7 +55,7 @@ namespace MatterHackers.MatterControl.PrintLibrary var searchIcon = AggContext.StaticData.LoadIcon("icon_search_24x24.png", 16, 16, theme.InvertIcons).AjustAlpha(0.3); - searchBox = new SearchInputBox(theme) + searchBox = new TextEditWithInlineCancel(theme) { Name = "Search", HAnchor = HAnchor.Stretch, @@ -64,7 +64,7 @@ namespace MatterHackers.MatterControl.PrintLibrary searchBox.ResetButton.Visible = false; - var searchInput = searchBox.searchInput; + var searchInput = searchBox.TextEditWidget; searchInput.BeforeDraw += (s, e) => { @@ -91,7 +91,7 @@ namespace MatterHackers.MatterControl.PrintLibrary } }; - searchBox.searchInput.ActualTextEditWidget.TextChanged += (s, e) => + searchBox.TextEditWidget.ActualTextEditWidget.TextChanged += (s, e) => { if (string.IsNullOrWhiteSpace(searchBox.Text)) { diff --git a/MatterControlLib/PartPreviewWindow/MainViewWidget.cs b/MatterControlLib/PartPreviewWindow/MainViewWidget.cs index 777ccd1e7..df00b20b8 100644 --- a/MatterControlLib/PartPreviewWindow/MainViewWidget.cs +++ b/MatterControlLib/PartPreviewWindow/MainViewWidget.cs @@ -335,6 +335,9 @@ namespace MatterHackers.MatterControl.PartPreviewWindow tabControl.TabBar.ActionArea.AddChild(brandMenu, 0); + tabControl.TabBar.ActionArea.VAnchor = VAnchor.Absolute; + tabControl.TabBar.ActionArea.Height = brandMenu.Height; + // Restore active workspace tabs foreach (var workspace in ApplicationController.Instance.Workspaces) { @@ -363,7 +366,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow HAnchor = HAnchor.Stretch, VAnchor = VAnchor.Absolute, Padding = 1, - Height = 22, + Height = 22 * GuiWidget.DeviceScale, BackgroundColor = theme.BackgroundColor, Border = new BorderDouble(top: 1), BorderColor = theme.BorderColor20, @@ -390,17 +393,15 @@ namespace MatterHackers.MatterControl.PartPreviewWindow Border = new BorderDouble(1), BackgroundColor = theme.MinimalShade.WithAlpha(10), BorderColor = theme.SlightShade, - Width = 200 + Width = 200 * GuiWidget.DeviceScale }; statusBar.AddChild(stretchStatusPanel); var panelBackgroundColor = theme.MinimalShade.WithAlpha(10); - statusBar.AddChild( - this.CreateThemeStatusPanel(theme, panelBackgroundColor)); + statusBar.AddChild(this.CreateThemeStatusPanel(theme, panelBackgroundColor)); - statusBar.AddChild( - this.CreateNetworkStatusPanel(theme)); + statusBar.AddChild(this.CreateNetworkStatusPanel(theme)); this.RenderRunningTasks(theme, ApplicationController.Instance.Tasks); } @@ -584,7 +585,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow Border = new BorderDouble(1), BackgroundColor = theme.MinimalShade.WithAlpha(10), BorderColor = theme.SlightShade, - Width = 120 + Width = 120 * GuiWidget.DeviceScale }; if (ApplicationController.ServicesStatusType != null) { @@ -633,7 +634,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { HAnchor = HAnchor.Absolute, VAnchor = VAnchor.Fit, - Width = 650, + Width = 650 * GuiWidget.DeviceScale, Border = 1, BorderColor = theme.DropList.Open.BackgroundColor, // Padding = theme.DefaultContainerPadding, @@ -818,7 +819,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow private static void EnableReduceWidth(ChromeTab partTab, ThemeConfig theme) { var scale = GuiWidget.DeviceScale; - partTab.MinimumSize = new Vector2(80 * scale, theme.TabButtonHeight); + partTab.MinimumSize = new Vector2(80 * scale, theme.TabButtonHeight * GuiWidget.DeviceScale); var textWidget = partTab.Descendants().First(); var tabPill = partTab.Descendants().First(); @@ -944,7 +945,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow Border = new BorderDouble(1), BorderColor = theme.SlightShade, ProgressBackgroundColor = progressBackgroundColor, - Width = 200 + Width = 200 * GuiWidget.DeviceScale }; tasksContainer.AddChild(runningTaskPanel); diff --git a/MatterControlLib/PartPreviewWindow/RunningTasksWidget.cs b/MatterControlLib/PartPreviewWindow/RunningTasksWidget.cs index 7e9aeb066..9279971f3 100644 --- a/MatterControlLib/PartPreviewWindow/RunningTasksWidget.cs +++ b/MatterControlLib/PartPreviewWindow/RunningTasksWidget.cs @@ -62,7 +62,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow BackgroundColor = theme.InteractionLayerOverlayColor, HAnchor = HAnchor.Fit | HAnchor.Left, VAnchor = VAnchor.Fit, - MinimumSize = new Vector2(325, 0), + MinimumSize = new Vector2(325 * GuiWidget.DeviceScale, 0), Border = new BorderDouble(top: 1), BorderColor = borderColor, }; diff --git a/MatterControlLib/PartPreviewWindow/SearchPanel.cs b/MatterControlLib/PartPreviewWindow/SearchPanel.cs index 8f8cb1b94..de57cfc4a 100644 --- a/MatterControlLib/PartPreviewWindow/SearchPanel.cs +++ b/MatterControlLib/PartPreviewWindow/SearchPanel.cs @@ -45,7 +45,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { private ChromeTabs tabControl; private GuiWidget searchButton; - private SearchInputBox searchBox; + private TextEditWithInlineCancel searchBox; public SearchPanel(ChromeTabs tabControl, GuiWidget searchButton, ThemeConfig theme) : base(theme, GrabBarSide.Left) @@ -67,13 +67,13 @@ namespace MatterHackers.MatterControl.PartPreviewWindow VAnchor = VAnchor.Stretch }; - searchBox = new SearchInputBox(theme) + searchBox = new TextEditWithInlineCancel(theme) { HAnchor = HAnchor.Stretch, VAnchor = VAnchor.Fit, Margin = new BorderDouble(5, 8, 5, 5) }; - searchBox.searchInput.ActualTextEditWidget.EnterPressed += async (s2, e2) => + searchBox.TextEditWidget.ActualTextEditWidget.EnterPressed += async (s2, e2) => { searchResults.CloseAllChildren(); @@ -87,7 +87,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow var searchHits = await Task.Run(() => { - return HelpIndex.Search(searchBox.searchInput.Text); + return HelpIndex.Search(searchBox.TextEditWidget.Text); }); searchResults.CloseAllChildren(); @@ -118,7 +118,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow searchBox.ResetButton.Click += (s2, e2) => { searchBox.BackgroundColor = Color.Transparent; - searchBox.searchInput.Text = ""; + searchBox.TextEditWidget.Text = ""; searchResults.CloseAllChildren(); }; @@ -140,7 +140,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow public override void OnLoad(EventArgs args) { // Set initial focus to input field - searchBox.searchInput.Focus(); + searchBox.TextEditWidget.Focus(); base.OnLoad(args); } @@ -174,7 +174,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow if (helpDocsTab.TabContent is HelpTreePanel treePanel) { - treePanel.MatchingText = searchBox.searchInput.Text; + treePanel.MatchingText = searchBox.TextEditWidget.Text; treePanel.ActiveNodePath = (sender as HelpSearchResultRow).SearchResult.Path; } } diff --git a/MatterControlLib/PartPreviewWindow/SearchableSectionWidget.cs b/MatterControlLib/PartPreviewWindow/SearchableSectionWidget.cs index 2b6f887fb..29eb23b23 100644 --- a/MatterControlLib/PartPreviewWindow/SearchableSectionWidget.cs +++ b/MatterControlLib/PartPreviewWindow/SearchableSectionWidget.cs @@ -40,38 +40,38 @@ namespace MatterHackers.MatterControl.CustomWidgets { public event EventHandler SearchInvoked; - private SearchInputBox searchPanel; + private TextEditWithInlineCancel searchPanel; public SearchableSectionWidget(string sectionTitle, GuiWidget sectionContent, ThemeConfig theme, int headingPointSize = -1, bool expandingContent = true, bool expanded = true, string serializationKey = null, bool defaultExpansion = false, bool setContentVAnchor = true, string emptyText = null) : base(sectionTitle, sectionContent, theme, theme.CreateSearchButton(), headingPointSize, expandingContent, expanded, serializationKey, defaultExpansion, setContentVAnchor) { var headerRow = this.Children.First(); - searchPanel = new SearchInputBox(theme, emptyText) + searchPanel = new TextEditWithInlineCancel(theme, emptyText) { Visible = false, BackgroundColor = theme.TabBarBackground, MinimumSize = new Vector2(0, headerRow.Height) }; - searchPanel.searchInput.Margin = new BorderDouble(3, 0); + searchPanel.TextEditWidget.Margin = new BorderDouble(3, 0); - searchPanel.searchInput.ActualTextEditWidget.EnterPressed += (s, e) => + searchPanel.TextEditWidget.ActualTextEditWidget.EnterPressed += (s, e) => { - var filter = searchPanel.searchInput.Text.Trim(); + var filter = searchPanel.TextEditWidget.Text.Trim(); this.SearchInvoked?.Invoke(this, new StringEventArgs(filter)); searchPanel.Visible = false; headerRow.Visible = true; - searchPanel.searchInput.Text = ""; + searchPanel.TextEditWidget.Text = ""; }; searchPanel.ResetButton.Click += (s, e) => { searchPanel.Visible = false; headerRow.Visible = true; - searchPanel.searchInput.Text = ""; + searchPanel.TextEditWidget.Text = ""; }; var searchButton = this.rightAlignedContent as GuiWidget; diff --git a/MatterControlLib/PartPreviewWindow/View3D/PrinterBar/OverflowBar.cs b/MatterControlLib/PartPreviewWindow/View3D/PrinterBar/OverflowBar.cs index e0f47a140..a100d9646 100644 --- a/MatterControlLib/PartPreviewWindow/View3D/PrinterBar/OverflowBar.cs +++ b/MatterControlLib/PartPreviewWindow/View3D/PrinterBar/OverflowBar.cs @@ -43,8 +43,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { public class OverflowBar : Toolbar { - protected static HashSet ignoredTypes = new HashSet { typeof(HorizontalLine), typeof(SearchInputBox) }; - protected static HashSet ignoredInMenuTypes = new HashSet { typeof(VerticalLine), typeof(HorizontalLine), typeof(SearchInputBox), typeof(HorizontalSpacer) }; + protected static HashSet ignoredTypes = new HashSet { typeof(HorizontalLine), typeof(TextEditWithInlineCancel) }; + protected static HashSet ignoredInMenuTypes = new HashSet { typeof(VerticalLine), typeof(HorizontalLine), typeof(TextEditWithInlineCancel), typeof(HorizontalSpacer) }; public OverflowBar(ThemeConfig theme) : this(null, theme) diff --git a/MatterControlLib/PartPreviewWindow/View3D/PrinterBar/PrintPopupMenu.cs b/MatterControlLib/PartPreviewWindow/View3D/PrinterBar/PrintPopupMenu.cs index f56396dcd..fcc0c4d72 100644 --- a/MatterControlLib/PartPreviewWindow/View3D/PrinterBar/PrintPopupMenu.cs +++ b/MatterControlLib/PartPreviewWindow/View3D/PrinterBar/PrintPopupMenu.cs @@ -92,7 +92,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow HAnchor = HAnchor.Fit | HAnchor.Left, VAnchor = VAnchor.Fit, Padding = 5, - MinimumSize = new Vector2(400, 65), + MinimumSize = new Vector2(400 * GuiWidget.DeviceScale, 65 * GuiWidget.DeviceScale), }; printPanel.AddChild(optionsPanel); diff --git a/MatterControlLib/PrinterCommunication/PrinterConnection.cs b/MatterControlLib/PrinterCommunication/PrinterConnection.cs index 4429547af..cad5bde7f 100644 --- a/MatterControlLib/PrinterCommunication/PrinterConnection.cs +++ b/MatterControlLib/PrinterCommunication/PrinterConnection.cs @@ -2268,6 +2268,7 @@ Make sure that your printer is turned on. Some printers will appear to be connec ActivePrintTask.PrintEnd = DateTime.Now; ActivePrintTask.PrintComplete = false; ActivePrintTask.PrintingGCodeFileName = ""; + ActivePrintTask.PrintCanceled = true; ActivePrintTask.Commit(); } diff --git a/MatterControlLib/SlicerConfiguration/Settings/PrinterSettingsExtensions.cs b/MatterControlLib/SlicerConfiguration/Settings/PrinterSettingsExtensions.cs index ada6ad399..809aa3d66 100644 --- a/MatterControlLib/SlicerConfiguration/Settings/PrinterSettingsExtensions.cs +++ b/MatterControlLib/SlicerConfiguration/Settings/PrinterSettingsExtensions.cs @@ -35,7 +35,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration { public static class PrinterSettingsExtensions { - private static Dictionary blackListSettings = new Dictionary() + private static readonly Dictionary BlackListSettings = new Dictionary() { [SettingsKey.spiral_vase] = "0", [SettingsKey.layer_to_pause] = "", @@ -48,7 +48,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration [SettingsKey.filament_1_has_been_loaded] = "0" }; - private static object writeLock = new object(); + private static readonly object WriteLock = new object(); public static double XSpeed(this PrinterSettings printerSettings) { @@ -82,7 +82,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration public static void ClearBlackList(this PrinterSettings settings) { - foreach (var kvp in blackListSettings) + foreach (var kvp in BlackListSettings) { if (settings.UserLayer.ContainsKey(kvp.Key)) { @@ -101,15 +101,19 @@ namespace MatterHackers.MatterControl.SlicerConfiguration return; } - settings.Save( - ProfileManager.Instance.ProfilePath(settings.ID), - userDrivenChange); + var profilePath = ProfileManager.Instance.ProfilePath(settings.ID); + + // it is possible for a profile to get deleted while printing so we have to check for it + if (profilePath != null) + { + settings.Save(profilePath, userDrivenChange); + } } public static void Save(this PrinterSettings settings, string filePath, bool userDrivenChange = true) { // TODO: Rewrite to be owned by ProfileManager and simply mark as dirty and every n period persist and clear dirty flags - lock (writeLock) + lock (WriteLock) { string json = settings.ToJson(); diff --git a/MatterControlLib/SlicerConfiguration/Settings/ProfileManager.cs b/MatterControlLib/SlicerConfiguration/Settings/ProfileManager.cs index 40ae5fa2d..1a18992fd 100644 --- a/MatterControlLib/SlicerConfiguration/Settings/ProfileManager.cs +++ b/MatterControlLib/SlicerConfiguration/Settings/ProfileManager.cs @@ -230,10 +230,17 @@ namespace MatterHackers.MatterControl.SlicerConfiguration [JsonIgnore] public IEnumerable ActiveProfiles => Profiles.Where(profile => !profile.MarkedForDelete).ToList(); + public static bool DebugPrinterDelete { get; set; } = false; + public PrinterInfo this[string profileID] { get { + if (DebugPrinterDelete) + { + return null; + } + return Profiles.Where(p => p.ID == profileID).FirstOrDefault(); } } @@ -279,7 +286,14 @@ namespace MatterHackers.MatterControl.SlicerConfiguration public string ProfilePath(string printerID) { - return ProfilePath(this[printerID]); + var printer = this[printerID]; + if (printer != null) + { + return ProfilePath(printer); + } + + // the printer may have been deleted + return null; } public string ProfilePath(PrinterInfo printer) diff --git a/MatterControlLib/SlicerConfiguration/SlicePresetsWindow/SlicePresetsPage.cs b/MatterControlLib/SlicerConfiguration/SlicePresetsWindow/SlicePresetsPage.cs index 6ecd343b0..67b75c574 100644 --- a/MatterControlLib/SlicerConfiguration/SlicePresetsWindow/SlicePresetsPage.cs +++ b/MatterControlLib/SlicerConfiguration/SlicePresetsWindow/SlicePresetsPage.cs @@ -62,11 +62,19 @@ namespace MatterHackers.MatterControl.SlicerConfiguration contentRow.BackgroundColor = Color.Transparent; - var inlineNameEdit = new InlineStringEdit(presetsContext.PersistenceLayer.Name, theme, presetsContext.LayerType.ToString() + " Name", boldFont: true); + var inlineNameEdit = new InlineStringEdit(presetsContext.PersistenceLayer.Name, + theme, + presetsContext.LayerType.ToString() + " Name", + boldFont: true, + emptyText: "Setting Name".Localize()); inlineNameEdit.ValueChanged += (s, e) => { printer.Settings.SetValue(SettingsKey.layer_name, inlineNameEdit.Text, presetsContext.PersistenceLayer); }; + inlineNameEdit.Closed += (s, e) => + { + printer.Settings.SetValue(SettingsKey.layer_name, inlineNameEdit.Text, presetsContext.PersistenceLayer); + }; contentRow.AddChild(inlineNameEdit); var sliceSettingsWidget = CreateSliceSettingsWidget(printer, presetsContext.PersistenceLayer); diff --git a/MatterControlLib/SlicerConfiguration/SliceSettingsWidget.cs b/MatterControlLib/SlicerConfiguration/SliceSettingsWidget.cs index 16559d548..acfcac1d9 100644 --- a/MatterControlLib/SlicerConfiguration/SliceSettingsWidget.cs +++ b/MatterControlLib/SlicerConfiguration/SliceSettingsWidget.cs @@ -110,7 +110,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration private readonly SettingsContext settingsContext; private readonly bool isPrimarySettingsView; - private readonly SearchInputBox searchPanel; + private readonly TextEditWithInlineCancel settingsNameEdit; private int groupPanelCount = 0; private List<(GuiWidget widget, SliceSettingData settingData)> settingsRows; private TextWidget filteredItemsHeading; @@ -135,17 +135,17 @@ namespace MatterHackers.MatterControl.SlicerConfiguration this.TabBar.Padding = this.TabBar.Margin.Clone(right: theme.ToolbarPadding.Right); - searchPanel = new SearchInputBox(theme) + settingsNameEdit = new TextEditWithInlineCancel(theme, "name".Localize()) { Visible = false, BackgroundColor = theme.TabBarBackground, MinimumSize = new Vector2(0, this.TabBar.Height) }; - searchPanel.searchInput.Margin = new BorderDouble(3, 0); - searchPanel.searchInput.ActualTextEditWidget.EnterPressed += (s, e) => + settingsNameEdit.TextEditWidget.Margin = new BorderDouble(3, 0); + settingsNameEdit.TextEditWidget.ActualTextEditWidget.EnterPressed += (s, e) => { - var filter = searchPanel.searchInput.Text.Trim(); + var filter = settingsNameEdit.TextEditWidget.Text.Trim(); foreach (var item in this.settingsRows) { @@ -158,16 +158,17 @@ namespace MatterHackers.MatterControl.SlicerConfiguration this.ShowFilteredView(); }; - searchPanel.ResetButton.Click += (s, e) => + + settingsNameEdit.ResetButton.Click += (s, e) => { - searchPanel.Visible = false; - searchPanel.searchInput.Text = ""; + settingsNameEdit.Visible = false; + settingsNameEdit.TextEditWidget.Text = ""; this.ClearFilter(); }; // Add heading for My Settings view - searchPanel.AddChild(filteredItemsHeading = new TextWidget(justMySettingsTitle, pointSize: theme.DefaultFontSize, textColor: theme.TextColor) + settingsNameEdit.AddChild(filteredItemsHeading = new TextWidget(justMySettingsTitle, pointSize: theme.DefaultFontSize, textColor: theme.TextColor) { Margin = new BorderDouble(left: 10), HAnchor = HAnchor.Left, @@ -175,7 +176,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration Visible = false }, 0); - this.AddChild(searchPanel, 0); + this.AddChild(settingsNameEdit, 0); var scrollable = new ScrollableWidget(true) { @@ -286,10 +287,10 @@ namespace MatterHackers.MatterControl.SlicerConfiguration searchButton.Click += (s, e) => { filteredItemsHeading.Visible = false; - searchPanel.searchInput.Visible = true; + settingsNameEdit.TextEditWidget.Visible = true; - searchPanel.Visible = true; - searchPanel.searchInput.Focus(); + settingsNameEdit.Visible = true; + settingsNameEdit.TextEditWidget.Focus(); this.TabBar.Visible = false; }; @@ -914,9 +915,9 @@ namespace MatterHackers.MatterControl.SlicerConfiguration } filteredItemsHeading.Visible = true; - searchPanel.searchInput.Visible = false; + settingsNameEdit.TextEditWidget.Visible = false; this.TabBar.Visible = false; - searchPanel.Visible = true; + settingsNameEdit.Visible = true; this.ShowFilteredView(); } diff --git a/Submodules/MatterSlice b/Submodules/MatterSlice index fd058b1fb..cdb91e5d4 160000 --- a/Submodules/MatterSlice +++ b/Submodules/MatterSlice @@ -1 +1 @@ -Subproject commit fd058b1fb03b30fc8b5f4a602d585aabc90809f9 +Subproject commit cdb91e5d4c50fd470a1e40aa2ab5ffdd0221071c diff --git a/Submodules/agg-sharp b/Submodules/agg-sharp index 04fb5d5d6..a3577e139 160000 --- a/Submodules/agg-sharp +++ b/Submodules/agg-sharp @@ -1 +1 @@ -Subproject commit 04fb5d5d6842ba55940bbfb1ffc5a13d2966a063 +Subproject commit a3577e13914d8f4cd0875d7848b2049af540fe74 diff --git a/Tests/MatterControl.AutomationTests/PrintingTests.cs b/Tests/MatterControl.AutomationTests/PrintingTests.cs index be6be659b..6fcd8da78 100644 --- a/Tests/MatterControl.AutomationTests/PrintingTests.cs +++ b/Tests/MatterControl.AutomationTests/PrintingTests.cs @@ -94,14 +94,10 @@ namespace MatterHackers.MatterControl.Tests.Automation { Assert.AreEqual(1, ApplicationController.Instance.ActivePrinters.Count(), "One printer should be defined after add"); - testRunner.OpenPrintPopupMenu(); - - testRunner.ClickByName("SetupPrinter"); - - testRunner.Complete9StepLeveling(); - - // print a part - testRunner.AddItemToBedplate(); + testRunner.OpenPrintPopupMenu() + .ClickByName("SetupPrinter") + .Complete9StepLeveling() + .AddItemToBedplate(); var printer = testRunner.FirstPrinter(); @@ -320,6 +316,33 @@ namespace MatterHackers.MatterControl.Tests.Automation }, maxTimeToRun: 90); } + [Test, Category("Emulator")] + public async Task PrinterDeletedWhilePrinting() + { + await MatterControlUtilities.RunTest((testRunner) => + { + using (var emulator = testRunner.LaunchAndConnectToPrinterEmulator()) + { + Assert.AreEqual(1, ApplicationController.Instance.ActivePrinters.Count(), "One printer should exist after add"); + + var printer = testRunner.FirstPrinter(); + + // print a part + testRunner.AddItemToBedplate(); + testRunner.StartPrint(pauseAtLayers: "2"); + ProfileManager.DebugPrinterDelete = true; + + // Wait for pause dialog + testRunner.ClickResumeButton(printer, true, 1); + + // Wait for done + testRunner.WaitForPrintFinished(printer); + } + + return Task.CompletedTask; + }, maxTimeToRun: 180); + } + [Test, Category("Emulator")] public async Task PrinterRecoveryTest() { @@ -333,67 +356,30 @@ namespace MatterHackers.MatterControl.Tests.Automation printer.Settings.SetValue(SettingsKey.recover_is_enabled, "1"); printer.Settings.SetValue(SettingsKey.has_hardware_leveling, "0"); - // TODO: Delay needed to work around timing issue in MatterHackers/MCCentral#2415 - testRunner.Delay(1); - - testRunner.WaitForReloadAll(() => { }); - Assert.IsTrue(printer.Connection.RecoveryIsEnabled); // print a part - testRunner.AddItemToBedplate(); - testRunner.StartPrint(pauseAtLayers: "2;4;6"); - - // Wait for pause dialog - testRunner.WaitForName("Yes Button", 15); // the yes button is 'Resume' - - // validate the current layer - Assert.AreEqual(1, printer.Connection.CurrentlyPrintingLayer); - - // Resume - testRunner.ClickByName("Yes Button"); - - // the printer is now paused - // close the pause dialog pop-up do not resume - ClickDialogButton(testRunner, printer, "No Button", 3); - - // Disconnect - testRunner.ClickByName("Disconnect from printer button"); - - // Reconnect - testRunner.WaitForName("Connect to printer button", 10); - testRunner.ClickByName("Connect to printer button"); - - testRunner.WaitFor(() => printer.Connection.CommunicationState == CommunicationStates.Connected); + testRunner.AddItemToBedplate() + .StartPrint(pauseAtLayers: "2;4;6") + .ClickResumeButton(printer, true, 1) // Resume + .ClickResumeButton(printer, false, 3) // close the pause dialog pop-up do not resume + .ClickByName("Disconnect from printer button") + .ClickByName("Connect to printer button") // Reconnect + .WaitFor(() => printer.Connection.CommunicationState == CommunicationStates.Connected); // Assert that recovery happens Assert.IsTrue(PrintRecovery.RecoveryAvailable(printer), "Recovery should be enabled after Disconnect while printing"); // Recover the print - ClickDialogButton(testRunner, printer, "Yes Button", -1); - - // The first pause that we get after recovery should be layer 6. - // wait for the pause and continue - ClickDialogButton(testRunner, printer, "Yes Button", 5); - - // Wait for done - testRunner.WaitForPrintFinished(printer); + testRunner.ClickButton("Yes Button", "Recover Print") + .ClickResumeButton(printer, true, 5) // The first pause that we get after recovery should be layer 6. + .WaitForPrintFinished(printer); } return Task.CompletedTask; }, maxTimeToRun: 180); } - // TODO: convert to extension method - private static void ClickDialogButton(AutomationRunner testRunner, PrinterConfig printer, string buttonName, int expectedLayer) - { - testRunner.WaitForName(buttonName, 90); - Assert.AreEqual(expectedLayer, printer.Connection.CurrentlyPrintingLayer); - - testRunner.ClickByName(buttonName); - testRunner.WaitFor(() => !testRunner.NameExists(buttonName), 1); - } - [Test, Category("Emulator")] public async Task TuningAdjustmentsDefaultToOneAndPersists() { @@ -421,23 +407,20 @@ namespace MatterHackers.MatterControl.Tests.Automation printFinishedResetEvent.Set(); }; - testRunner.StartPrint(); - - testRunner.ScrollIntoView("Extrusion Multiplier NumberEdit"); - testRunner.ScrollIntoView("Feed Rate NumberEdit"); + testRunner.StartPrint() + .ScrollIntoView("Extrusion Multiplier NumberEdit") + .ScrollIntoView("Feed Rate NumberEdit"); // Tuning values should default to 1 when missing ConfirmExpectedSpeeds(testRunner, 1, 1, "Initial case"); - testRunner.Delay(); - testRunner.ClickByName("Extrusion Multiplier NumberEdit"); - testRunner.Type(targetExtrusionRate.ToString()); - - testRunner.ClickByName("Feed Rate NumberEdit"); - testRunner.Type(targetFeedRate.ToString()); - - // Force focus away from the feed rate field, causing an persisted update - testRunner.ClickByName("Extrusion Multiplier NumberEdit"); + testRunner.Delay() + .ClickByName("Extrusion Multiplier NumberEdit") + .Type(targetExtrusionRate.ToString()) + .ClickByName("Feed Rate NumberEdit") + .Type(targetFeedRate.ToString()) + // Force focus away from the feed rate field, causing an persisted update + .ClickByName("Extrusion Multiplier NumberEdit"); ConfirmExpectedSpeeds(testRunner, targetExtrusionRate, targetFeedRate, "After setting TextEdit values"); @@ -453,17 +436,15 @@ namespace MatterHackers.MatterControl.Tests.Automation // Values should match entered values ConfirmExpectedSpeeds(testRunner, targetExtrusionRate, targetFeedRate, "After print finished"); - testRunner.WaitForPrintFinished(printer); - - // Restart the print - testRunner.StartPrint(); - testRunner.Delay(1); + testRunner.WaitForPrintFinished(printer) + .StartPrint() // Restart the print + .Delay(1); // Values should match entered values ConfirmExpectedSpeeds(testRunner, targetExtrusionRate, targetFeedRate, "After print restarted"); - testRunner.CancelPrint(); - testRunner.Delay(1); + testRunner.CancelPrint() + .Delay(1); // Values should match entered values ConfirmExpectedSpeeds(testRunner, targetExtrusionRate, targetFeedRate, "After canceled print"); diff --git a/Tests/MatterControl.Tests/MatterControl/MatterControlUtilities.cs b/Tests/MatterControl.Tests/MatterControl/MatterControlUtilities.cs index 1ec541845..649de067a 100644 --- a/Tests/MatterControl.Tests/MatterControl/MatterControlUtilities.cs +++ b/Tests/MatterControl.Tests/MatterControl/MatterControlUtilities.cs @@ -62,7 +62,7 @@ namespace MatterHackers.MatterControl.Tests.Automation { private static bool saveImagesForDebug = true; - private static event EventHandler unregisterEvents; + private static event EventHandler UnregisterEvents; private static int testID = 0; @@ -118,7 +118,7 @@ namespace MatterHackers.MatterControl.Tests.Automation { // Wire up a block and release mechanism to wait until the sign in process has completed AutoResetEvent resetEvent = new AutoResetEvent(false); - ApplicationController.Instance.DoneReloadingAll.RegisterEvent((s, e) => resetEvent.Set(), ref unregisterEvents); + ApplicationController.Instance.DoneReloadingAll.RegisterEvent((s, e) => resetEvent.Set(), ref UnregisterEvents); // Start the procedure that begins a ReloadAll event in MatterControl reloadAllAction(); @@ -127,13 +127,13 @@ namespace MatterHackers.MatterControl.Tests.Automation resetEvent.WaitOne(10 * 1000); // Remove our DoneReloadingAll listener - unregisterEvents(null, null); + UnregisterEvents(null, null); // Wait for any post DoneReloadingAll code to finish up and return testRunner.Delay(.2); } - public static void WaitForPage(this AutomationRunner testRunner, string headerText) + public static AutomationRunner WaitForPage(this AutomationRunner testRunner, string headerText) { // Helper methods bool HeaderExists(string text) @@ -147,8 +147,9 @@ namespace MatterHackers.MatterControl.Tests.Automation testRunner.WaitFor(() => HeaderExists(headerText)); Assert.IsTrue(HeaderExists(headerText), "Expected page not found: " + headerText); - } + return testRunner; + } public static string PathToExportGcodeFolder { @@ -168,7 +169,8 @@ namespace MatterHackers.MatterControl.Tests.Automation public enum PrepAction { CloseSignInAndPrinterSelect - }; + } +; public static void ExpandEditTool(this AutomationRunner testRunner, string expandCheckboxButtonName) { @@ -186,6 +188,7 @@ namespace MatterHackers.MatterControl.Tests.Automation { testRunner.ClickByName("3D View Edit"); } + testRunner.DragDropByName("InteractionLayer", "InteractionLayer", offsetDrop: new Agg.Point2D(10, 15), mouseButtons: MouseButtons.Right); testRunner.Delay(1); @@ -252,23 +255,16 @@ namespace MatterHackers.MatterControl.Tests.Automation WaitForTempStream.WaitAfterReachTempTime = config.TempStabilizationTime; // Create the printer - testRunner.AddAndSelectPrinter(make, model); - - // edit the com port - testRunner.SwitchToPrinterSettings(); + testRunner.AddAndSelectPrinter(make, model) + .SwitchToPrinterSettings(); var serialPortDropDown = testRunner.GetWidgetByName("com_port Field", out _, 1); testRunner.WaitFor(() => serialPortDropDown.Enabled); // Wait until the serialPortDropDown is ready to click it. Ensures the printer is loaded. - - testRunner.ClickByName("com_port Field"); - - testRunner.ClickByName("Emulator Menu Item"); - - // connect to the created printer - testRunner.ClickByName("Connect to printer button"); - - testRunner.WaitForName("Disconnect from printer button"); + testRunner.ClickByName("com_port Field") + .ClickByName("Emulator Menu Item") + .ClickByName("Connect to printer button") // connect to the created printer + .WaitForName("Disconnect from printer button"); // replace the old behavior of clicking the 'Already Loaded' button by setting to filament_has_been_loaded. ApplicationController.Instance.ActivePrinters.First().Settings.SetValue(SettingsKey.filament_has_been_loaded, "1"); @@ -279,22 +275,22 @@ namespace MatterHackers.MatterControl.Tests.Automation return Emulator.Instance; } - public static void CancelPrint(this AutomationRunner testRunner) + public static AutomationRunner CancelPrint(this AutomationRunner testRunner) { // If the pause/resume dialog is open, dismiss it before canceling the print - if (testRunner.WaitForName("Yes Button", 1)) + if (testRunner.NamedWidgetExists("Yes Button")) { testRunner.ClickByName("Yes Button"); } - testRunner.WaitForWidgetEnabled("Print Progress Dial", 15); + testRunner.WaitForWidgetEnabled("Print Progress Dial", 15) + .WaitForWidgetEnabled("Stop Task Button") + .ClickByName("Stop Task Button") + .WaitForName("Ok Button", 10); // Wait for and dismiss the new PrintCompleted dialog - testRunner.WaitForWidgetEnabled("Stop Task Button"); - testRunner.ClickByName("Stop Task Button"); - - // Wait for and dismiss the new PrintCompleted dialog - testRunner.WaitForName("Ok Button", 10); testRunner.ClickByName("Ok Button"); + + return testRunner; } public static void WaitForLayer(this Emulator emulator, PrinterSettings printerSettings, int layerNumber, double secondsToWait = 30) @@ -343,16 +339,13 @@ namespace MatterHackers.MatterControl.Tests.Automation testRunner.ClickByName("Yes Button"); } - public static void AddAndSelectPrinter(this AutomationRunner testRunner, string make = "Airwolf 3D", string model = "HD") + public static AutomationRunner AddAndSelectPrinter(this AutomationRunner testRunner, string make = "Airwolf 3D", string model = "HD") { testRunner.GetWidgetByName("PartPreviewContent", out SystemWindow systemWindow, 10); - // make sure we wait for MC to be up and running - testRunner.WaitforDraw(systemWindow); + testRunner.WaitforDraw(systemWindow) // make sure we wait for MC to be up and running + .EnsureWelcomePageClosed(); // close the welcome message - // close the welcome message - testRunner.EnsureWelcomePageClosed(); - testRunner.Delay(); if (testRunner.NamedWidgetExists("Cancel Wizard Button")) { testRunner.ClickByName("Cancel Wizard Button"); @@ -388,26 +381,21 @@ namespace MatterHackers.MatterControl.Tests.Automation }); // Apply filter - testRunner.ClickByName("Search"); - testRunner.Type(model); - testRunner.Type("{Enter}"); + testRunner.ClickByName("Search") + .Type(model) + .Type("{Enter}") + .Delay() + .ClickByName($"Node{make}{model}") // Click printer node + .ClickByName("Next Button") // Continue to next page + .Delay() + .WaitFor(() => testRunner.ChildExists()); + testRunner.ClickByName("Cancel Wizard Button") + .WaitFor(() => !testRunner.ChildExists()); - // Click printer node - testRunner.Delay(); - testRunner.ClickByName($"Node{make}{model}"); - - // Continue to next page - testRunner.ClickByName("Next Button"); - - testRunner.Delay(); - - testRunner.WaitFor(() => testRunner.ChildExists()); - testRunner.ClickByName("Cancel Wizard Button"); - - testRunner.WaitFor(() => !testRunner.ChildExists()); + return testRunner; } - public static void EnsureWelcomePageClosed(this AutomationRunner testRunner) + public static AutomationRunner EnsureWelcomePageClosed(this AutomationRunner testRunner) { // Close the WelcomePage window if active if (testRunner.GetWidgetByName("HeaderRow", out _) is GuiWidget headerRow @@ -416,6 +404,10 @@ namespace MatterHackers.MatterControl.Tests.Automation { testRunner.ClickByName("Cancel Wizard Button"); } + + testRunner.WaitFor(() => !testRunner.NameExists("Cancel Wizard Button", .1)); + + return testRunner; } public static void WaitForAndCancelPrinterSetupPage(this AutomationRunner testRunner) @@ -511,6 +503,32 @@ namespace MatterHackers.MatterControl.Tests.Automation testRunner.ClickByName("User Options Menu"); } + public static AutomationRunner ClickButton(this AutomationRunner testRunner, string buttonName, string buttonText, double maxWait = 5) + { + testRunner.WaitForName(buttonName, maxWait, predicate: (w) => w.Children.FirstOrDefault().Text == buttonText); + testRunner.ClickByName(buttonName); + + return testRunner; + } + + public static AutomationRunner ClickResumeButton(this AutomationRunner testRunner, + PrinterConfig printer, + bool resume, + int expectedLayer) + { + var buttonName = resume ? "Yes Button" : "No Button"; + var buttonText = resume ? "Resume" : "OK"; + testRunner.WaitForName(buttonName, 90, predicate: (w) => w.Children.FirstOrDefault().Text == buttonText); + Assert.AreEqual(expectedLayer, + printer.Connection.CurrentlyPrintingLayer, + $"Expected the paused layer to be {expectedLayer} but was {printer.Connection.CurrentlyPrintingLayer}."); + + testRunner.ClickByName(buttonName); + testRunner.WaitFor(() => !testRunner.NameExists(buttonName), 2); + + return testRunner; + } + public static void NavigateToFolder(this AutomationRunner testRunner, string libraryRowItemName) { testRunner.EnsureContentMenuOpen(); @@ -594,8 +612,8 @@ namespace MatterHackers.MatterControl.Tests.Automation testRunner.ClickByName("SetupPrinter"); // Configure ABS as selected material - //testRunner.ClickByName("Material DropDown List"); - //testRunner.ClickByName("ABS Menu"); + // testRunner.ClickByName("Material DropDown List"); + // testRunner.ClickByName("ABS Menu"); // Currently material selection is not required, simply act of clicking 'Select' clears setup required testRunner.ClickByName("Already Loaded Button"); @@ -659,7 +677,7 @@ namespace MatterHackers.MatterControl.Tests.Automation testRunner.WaitForWidgetDisappear("Automation Dialog TextEdit", 5); } - public static void AddItemToBedplate(this AutomationRunner testRunner, string containerName = "Calibration Parts Row Item Collection", string partName = "Row Item Calibration - Box.stl") + public static AutomationRunner AddItemToBedplate(this AutomationRunner testRunner, string containerName = "Calibration Parts Row Item Collection", string partName = "Row Item Calibration - Box.stl") { if (!testRunner.NameExists(partName, 1) && !string.IsNullOrEmpty(containerName)) { @@ -671,6 +689,7 @@ namespace MatterHackers.MatterControl.Tests.Automation { testRunner.ClickByName(partName); } + testRunner.ClickByName("Print Library Overflow Menu"); var view3D = testRunner.GetWidgetByName("View3DWidget", out _) as View3DWidget; @@ -686,6 +705,8 @@ namespace MatterHackers.MatterControl.Tests.Automation { testRunner.WaitFor(() => scene.Children.LastOrDefault() as InsertionGroupObject3D != null, 10); } + + return testRunner; } public static void SaveBedplateToFolder(this AutomationRunner testRunner, string newFileName, string folderName) @@ -706,17 +727,19 @@ namespace MatterHackers.MatterControl.Tests.Automation testRunner.Delay(2); } - public static void WaitForPrintFinished(this AutomationRunner testRunner, PrinterConfig printer, int maxSeconds = 500) + public static AutomationRunner WaitForPrintFinished(this AutomationRunner testRunner, PrinterConfig printer, int maxSeconds = 500) { testRunner.WaitFor(() => printer.Connection.CommunicationState == CommunicationStates.FinishedPrint, maxSeconds); // click the ok button on the print complete dialog testRunner.ClickByName("Ok Button"); + + return testRunner; } /// /// Gets a reference to the first and only active printer. Throws if called when multiple active printers exists /// - /// + /// The AutomationRunner in use /// The first active printer public static PrinterConfig FirstPrinter(this AutomationRunner testRunner) { @@ -753,8 +776,8 @@ namespace MatterHackers.MatterControl.Tests.Automation string defaultTestImages = null) { // Walk back a step in the stack and output the callers name - //StackTrace st = new StackTrace(false); - //Debug.WriteLine("\r\n ***** Running automation test: {0} {1} ", st.GetFrames().Skip(1).First().GetMethod().Name, DateTime.Now); + // StackTrace st = new StackTrace(false); + // Debug.WriteLine("\r\n ***** Running automation test: {0} {1} ", st.GetFrames().Skip(1).First().GetMethod().Name, DateTime.Now); if (staticDataPathOverride == null) { @@ -771,7 +794,7 @@ namespace MatterHackers.MatterControl.Tests.Automation Environment.CurrentDirectory = TestContext.CurrentContext.ResolveProjectPath(5, "MatterControl", "bin", outputDirectory); // Override the default SystemWindow type without config.json - //AggContext.Config.ProviderTypes.SystemWindowProvider = "MatterHackers.Agg.UI.OpenGLWinformsWindowProvider, agg_platform_win32"; + // AggContext.Config.ProviderTypes.SystemWindowProvider = "MatterHackers.Agg.UI.OpenGLWinformsWindowProvider, agg_platform_win32"; AggContext.Config.ProviderTypes.SystemWindowProvider = "MatterHackers.MatterControl.WinformsSingleWindowProvider, MatterControl.Winforms"; #if !__ANDROID__ @@ -792,7 +815,7 @@ namespace MatterHackers.MatterControl.Tests.Automation } UserSettings.Instance.set(UserSettingsKey.ThumbnailRenderingMode, "orthographic"); - //GL.HardwareAvailable = false; + // GL.HardwareAvailable = false; var config = TestAutomationConfig.Load(); if (config.UseAutomationDialogs) @@ -891,12 +914,13 @@ namespace MatterHackers.MatterControl.Tests.Automation Environment.CurrentDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); } - public static void StartSlicing(this AutomationRunner testRunner) + public static AutomationRunner StartSlicing(this AutomationRunner testRunner) { testRunner.ClickByName("Generate Gcode Button"); + return testRunner; } - public static void OpenPrintPopupMenu(this AutomationRunner testRunner) + public static AutomationRunner OpenPrintPopupMenu(this AutomationRunner testRunner) { var printerConnection = ApplicationController.Instance.DragDropData.View3DWidget.Printer.Connection; @@ -912,13 +936,16 @@ namespace MatterHackers.MatterControl.Tests.Automation // Wait for child control testRunner.WaitForName("Start Print Button"); + return testRunner; } /// /// Open the Print popup menu and click the Start Print button /// - /// - public static void StartPrint(this AutomationRunner testRunner, string pauseAtLayers = null) + /// The AutomationRunner we are using. + /// The string to write into the pause field in the print menu. + /// + public static AutomationRunner StartPrint(this AutomationRunner testRunner, string pauseAtLayers = null) { // Open popup testRunner.OpenPrintPopupMenu(); @@ -932,6 +959,20 @@ namespace MatterHackers.MatterControl.Tests.Automation } testRunner.ClickByName("Start Print Button"); + + return testRunner; + } + + public static AutomationRunner WaitForPause(this AutomationRunner testRunner, PrinterConfig printer, int expectedLayer) + { + testRunner.WaitForName("Yes Button", 15, predicate: (w) => w.Children.FirstOrDefault().Text == "Resume"); + // validate the current layer + if (expectedLayer != printer.Connection.CurrentlyPrintingLayer) + { + throw new Exception($"Expected the paused layer to be {expectedLayer} but was {printer.Connection.CurrentlyPrintingLayer}."); + } + + return testRunner; } public static void OpenPrintPopupAdvanced(this AutomationRunner testRunner) @@ -960,7 +1001,7 @@ namespace MatterHackers.MatterControl.Tests.Automation /// /// Switch to the primary SliceSettings tab /// - /// + /// The AutomationRunner in use public static void SwitchToSliceSettings(this AutomationRunner testRunner) { EnsurePrinterSidebarOpen(testRunner); @@ -970,19 +1011,19 @@ namespace MatterHackers.MatterControl.Tests.Automation testRunner.ClickByName("Slice Settings Tab"); } - public static void Complete9StepLeveling(this AutomationRunner testRunner, int numUpClicks = 1) + public static AutomationRunner WaitForPageAndAdvance(this AutomationRunner testRunner, string headerText) { - void waitForPageAndAdvance(string headerText) - { - testRunner.WaitForPage(headerText); - testRunner.ClickByName("Next Button"); - } + testRunner.WaitForPage(headerText) + .ClickByName("Next Button"); - testRunner.Delay(); + return testRunner; + } - waitForPageAndAdvance("Print Leveling Overview"); - - waitForPageAndAdvance("Heating the printer"); + public static AutomationRunner Complete9StepLeveling(this AutomationRunner testRunner, int numUpClicks = 1) + { + testRunner.Delay() + .WaitForPageAndAdvance("Print Leveling Overview") + .WaitForPageAndAdvance("Heating the printer"); for (int i = 0; i < 3; i++) { @@ -995,19 +1036,17 @@ namespace MatterHackers.MatterControl.Tests.Automation testRunner.ClickByName("Move Z positive"); } - testRunner.WaitForPage($"Step {section} of 9"); - testRunner.ClickByName("Next Button"); - - testRunner.WaitForPage($"Step {section + 1} of 9"); - testRunner.ClickByName("Next Button"); - - testRunner.WaitForPage($"Step {section + 2} of 9"); - testRunner.ClickByName("Next Button"); + testRunner.WaitForPage($"Step {section} of 9") + .ClickByName("Next Button") + .WaitForPage($"Step {section + 1} of 9") + .ClickByName("Next Button") + .WaitForPage($"Step {section + 2} of 9") + .ClickByName("Next Button"); } - testRunner.ClickByName("Done Button"); + testRunner.ClickByName("Done Button") + .Delay(); - testRunner.Delay(); if (testRunner.NameExists("Already Loaded Button", 0.2)) { testRunner.ClickByName("Already Loaded Button"); @@ -1015,12 +1054,14 @@ namespace MatterHackers.MatterControl.Tests.Automation // Close the staged wizard window testRunner.ClickByName("Cancel Wizard Button"); + + return testRunner; } /// /// Switch to printer settings /// - /// + /// The AutomationRunner in use public static void SwitchToPrinterSettings(this AutomationRunner testRunner) { EnsurePrinterSidebarOpen(testRunner); @@ -1030,6 +1071,7 @@ namespace MatterHackers.MatterControl.Tests.Automation testRunner.ClickByName("Printer Overflow Menu"); testRunner.ClickByName("Configure Printer Menu Item"); } + testRunner.ClickByName("Printer Tab"); } @@ -1085,7 +1127,7 @@ namespace MatterHackers.MatterControl.Tests.Automation /// /// Switch to Printer -> Controls /// - /// + /// The AutomationRunner in use public static void SwitchToControlsTab(this AutomationRunner testRunner) { // Change to Printer Controls @@ -1103,7 +1145,7 @@ namespace MatterHackers.MatterControl.Tests.Automation /// /// Switch to Printer -> Terminal /// - /// + /// The AutomationRunner in use public static void SwitchToTerminalTab(this AutomationRunner testRunner) { // Change to Printer Controls @@ -1121,7 +1163,7 @@ namespace MatterHackers.MatterControl.Tests.Automation /// /// Switch to Printer -> GCode Tab - NOTE: as a short term hack this helper as adds content to the bed and slices to ensure GCode view options appear as expected /// - /// + /// The AutomationRunner in use public static void SwitchToGCodeTab(this AutomationRunner testRunner) { testRunner.ClickByName("Layers3D Button"); @@ -1145,7 +1187,7 @@ namespace MatterHackers.MatterControl.Tests.Automation /// /// Adds the given asset names to the local library and validates the result /// - /// + /// The AutomationRunner in use /// The test assets to add to the library public static void AddTestAssetsToLibrary(this AutomationRunner testRunner, IEnumerable assetNames, string targetLibrary = "Local Library Row Item Collection") { @@ -1183,7 +1225,7 @@ namespace MatterHackers.MatterControl.Tests.Automation /// /// Control clicks each specified item /// - /// + /// The AutomationRunner in use /// The widgets to click public static void SelectListItems(this AutomationRunner testRunner, params string[] widgetNames) { @@ -1193,6 +1235,7 @@ namespace MatterHackers.MatterControl.Tests.Automation { testRunner.ClickByName(widgetName); } + Keyboard.SetKeyDownState(Keys.ControlKey, down: false); } } @@ -1210,7 +1253,7 @@ namespace MatterHackers.MatterControl.Tests.Automation public class TestAutomationConfig { - private static readonly string configPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "MHTest.config"); + private static readonly string ConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "MHTest.config"); /// /// The ClientToken used by tests to emulate an external client @@ -1246,14 +1289,14 @@ namespace MatterHackers.MatterControl.Tests.Automation { TestAutomationConfig config = null; - if (!File.Exists(configPath)) + if (!File.Exists(ConfigPath)) { config = new TestAutomationConfig(); config.Save(); } else { - config = JsonConvert.DeserializeObject(File.ReadAllText(configPath)); + config = JsonConvert.DeserializeObject(File.ReadAllText(ConfigPath)); } return config; @@ -1264,7 +1307,7 @@ namespace MatterHackers.MatterControl.Tests.Automation /// public void Save() { - File.WriteAllText(configPath, JsonConvert.SerializeObject(this, Formatting.Indented)); + File.WriteAllText(ConfigPath, JsonConvert.SerializeObject(this, Formatting.Indented)); } } } \ No newline at end of file