Merge pull request #4806 from larsbrubaker/master

master
This commit is contained in:
Lars Brubaker 2020-07-19 20:01:08 -07:00 committed by GitHub
commit eef20b42b8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 413 additions and 328 deletions

View file

@ -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(),

View file

@ -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");
}
}
}

View file

@ -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();

View file

@ -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,
};

View file

@ -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;
}
}
}

View file

@ -325,6 +325,8 @@ namespace MatterHackers.MatterControl.DataStorage
public bool PrintComplete { get; set; }
public bool PrintCanceled { get; set; }
public DateTime PrintEnd { get; set; }
[Indexed]

View file

@ -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()

View file

@ -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);

View file

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

View file

@ -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);

View file

@ -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;
}
}
}

View file

@ -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))
{

View file

@ -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<TextWidget>().First();
var tabPill = partTab.Descendants<SimpleTab.TabPill>().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);

View file

@ -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,
};

View file

@ -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;
}
}

View file

@ -40,38 +40,38 @@ namespace MatterHackers.MatterControl.CustomWidgets
{
public event EventHandler<StringEventArgs> 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;

View file

@ -43,8 +43,8 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
{
public class OverflowBar : Toolbar
{
protected static HashSet<Type> ignoredTypes = new HashSet<Type> { typeof(HorizontalLine), typeof(SearchInputBox) };
protected static HashSet<Type> ignoredInMenuTypes = new HashSet<Type> { typeof(VerticalLine), typeof(HorizontalLine), typeof(SearchInputBox), typeof(HorizontalSpacer) };
protected static HashSet<Type> ignoredTypes = new HashSet<Type> { typeof(HorizontalLine), typeof(TextEditWithInlineCancel) };
protected static HashSet<Type> ignoredInMenuTypes = new HashSet<Type> { typeof(VerticalLine), typeof(HorizontalLine), typeof(TextEditWithInlineCancel), typeof(HorizontalSpacer) };
public OverflowBar(ThemeConfig theme)
: this(null, theme)

View file

@ -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);

View file

@ -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();
}

View file

@ -35,7 +35,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
{
public static class PrinterSettingsExtensions
{
private static Dictionary<string, string> blackListSettings = new Dictionary<string, string>()
private static readonly Dictionary<string, string> BlackListSettings = new Dictionary<string, string>()
{
[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();

View file

@ -230,10 +230,17 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
[JsonIgnore]
public IEnumerable<PrinterInfo> 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)

View file

@ -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);

View file

@ -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();
}

@ -1 +1 @@
Subproject commit fd058b1fb03b30fc8b5f4a602d585aabc90809f9
Subproject commit cdb91e5d4c50fd470a1e40aa2ab5ffdd0221071c

@ -1 +1 @@
Subproject commit 04fb5d5d6842ba55940bbfb1ffc5a13d2966a063
Subproject commit a3577e13914d8f4cd0875d7848b2049af540fe74

View file

@ -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");

View file

@ -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<SetupStepComPortOne>());
testRunner.ClickByName("Cancel Wizard Button")
.WaitFor(() => !testRunner.ChildExists<SetupStepComPortOne>());
// Click printer node
testRunner.Delay();
testRunner.ClickByName($"Node{make}{model}");
// Continue to next page
testRunner.ClickByName("Next Button");
testRunner.Delay();
testRunner.WaitFor(() => testRunner.ChildExists<SetupStepComPortOne>());
testRunner.ClickByName("Cancel Wizard Button");
testRunner.WaitFor(() => !testRunner.ChildExists<SetupStepComPortOne>());
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;
}
/// <summary>
/// Gets a reference to the first and only active printer. Throws if called when multiple active printers exists
/// </summary>
/// <param name="testRunner"></param>
/// <param name="testRunner">The AutomationRunner in use</param>
/// <returns>The first active printer</returns>
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;
}
/// <summary>
/// Open the Print popup menu and click the Start Print button
/// </summary>
/// <param name="testRunner"></param>
public static void StartPrint(this AutomationRunner testRunner, string pauseAtLayers = null)
/// <param name="testRunner">The AutomationRunner we are using.</param>
/// <param name="pauseAtLayers">The string to write into the pause field in the print menu.</param>
/// <returns></returns>
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
/// <summary>
/// Switch to the primary SliceSettings tab
/// </summary>
/// <param name="testRunner"></param>
/// <param name="testRunner">The AutomationRunner in use</param>
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;
}
/// <summary>
/// Switch to printer settings
/// </summary>
/// <param name="testRunner"></param>
/// <param name="testRunner">The AutomationRunner in use</param>
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
/// <summary>
/// Switch to Printer -> Controls
/// </summary>
/// <param name="testRunner"></param>
/// <param name="testRunner">The AutomationRunner in use</param>
public static void SwitchToControlsTab(this AutomationRunner testRunner)
{
// Change to Printer Controls
@ -1103,7 +1145,7 @@ namespace MatterHackers.MatterControl.Tests.Automation
/// <summary>
/// Switch to Printer -> Terminal
/// </summary>
/// <param name="testRunner"></param>
/// <param name="testRunner">The AutomationRunner in use</param>
public static void SwitchToTerminalTab(this AutomationRunner testRunner)
{
// Change to Printer Controls
@ -1121,7 +1163,7 @@ namespace MatterHackers.MatterControl.Tests.Automation
/// <summary>
/// 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
/// </summary>
/// <param name="testRunner"></param>
/// <param name="testRunner">The AutomationRunner in use</param>
public static void SwitchToGCodeTab(this AutomationRunner testRunner)
{
testRunner.ClickByName("Layers3D Button");
@ -1145,7 +1187,7 @@ namespace MatterHackers.MatterControl.Tests.Automation
/// <summary>
/// Adds the given asset names to the local library and validates the result
/// </summary>
/// <param name="testRunner"></param>
/// <param name="testRunner">The AutomationRunner in use</param>
/// <param name="assetNames">The test assets to add to the library</param>
public static void AddTestAssetsToLibrary(this AutomationRunner testRunner, IEnumerable<string> assetNames, string targetLibrary = "Local Library Row Item Collection")
{
@ -1183,7 +1225,7 @@ namespace MatterHackers.MatterControl.Tests.Automation
/// <summary>
/// Control clicks each specified item
/// </summary>
/// <param name="testRunner"></param>
/// <param name="testRunner">The AutomationRunner in use</param>
/// <param name="widgetNames">The widgets to click</param>
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");
/// <summary>
/// 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<TestAutomationConfig>(File.ReadAllText(configPath));
config = JsonConvert.DeserializeObject<TestAutomationConfig>(File.ReadAllText(ConfigPath));
}
return config;
@ -1264,7 +1307,7 @@ namespace MatterHackers.MatterControl.Tests.Automation
/// </summary>
public void Save()
{
File.WriteAllText(configPath, JsonConvert.SerializeObject(this, Formatting.Indented));
File.WriteAllText(ConfigPath, JsonConvert.SerializeObject(this, Formatting.Indented));
}
}
}