Merge pull request #4147 from jlewin/master

Validate passed in field rather than hard-coded start_gcode
This commit is contained in:
johnlewin 2019-01-05 11:19:14 -08:00 committed by GitHub
commit bcbb82d145
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 278 additions and 101 deletions

View file

@ -152,5 +152,15 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
public const string z_probe_z_offset = nameof(z_probe_z_offset);
public const string z_servo_depolyed_angle = nameof(z_servo_depolyed_angle);
public const string z_servo_retracted_angle = nameof(z_servo_retracted_angle);
public const string small_perimeter_speed = nameof(small_perimeter_speed);
public const string bridge_speed = nameof(bridge_speed);
public const string air_gap_speed = nameof(air_gap_speed);
public const string external_perimeter_speed = nameof(external_perimeter_speed);
public const string infill_speed = nameof(infill_speed);
public const string perimeter_speed = nameof(perimeter_speed);
public const string solid_infill_speed = nameof(solid_infill_speed);
public const string support_material_speed = nameof(support_material_speed);
public const string travel_speed = nameof(travel_speed);
public const string retract_speed = nameof(retract_speed);
}
}

View file

@ -2376,10 +2376,17 @@ If you experience adhesion problems, please re-run leveling."
// Project to newline separated Error/Details/Location string
var formattedErrors = errors.Select(err =>
{
string location = null;
if (err is SettingsValidationError settingsError)
{
location = settingsError.Location;
}
// Conditionally combine Error/Details/Location when not empty
return err.Error +
((string.IsNullOrWhiteSpace(err.Details)) ? "" : $"\n\n{err.Details}") +
((string.IsNullOrWhiteSpace(err.Location)) ? "" : $"\n\n{err.Location}");
((string.IsNullOrWhiteSpace(location)) ? "" : $"\n\n{location}");
}).ToArray();
StyledMessageBox.ShowMessageBox(

View file

@ -55,24 +55,23 @@ namespace MatterHackers.MatterControl
settings.GetValue<double>(SettingsKey.nozzle_diameter));
errors.Add(
new ValidationError()
new SettingsValidationError(SettingsKey.layer_height)
{
Error = "{0} must be less than or equal to the {1}.".Localize().FormatWith(
GetSettingsName(SettingsKey.layer_height), GetSettingsName(SettingsKey.nozzle_diameter)),
Details = details,
Location = GetSettingsLocation(SettingsKey.layer_height)
});
}
else if (settings.GetValue<double>(SettingsKey.layer_height) <= 0)
{
errors.Add(
new ValidationError()
new SettingsValidationError(SettingsKey.layer_height)
{
Error = "{0} must be greater than 0.".Localize().FormatWith(GetSettingsName(SettingsKey.layer_height)),
Location = GetSettingsLocation(SettingsKey.layer_height)
});
}
else if (settings.GetValue<double>(SettingsKey.first_layer_height) > settings.GetValue<double>(SettingsKey.nozzle_diameter))
if (settings.GetValue<double>(SettingsKey.first_layer_height) > settings.GetValue<double>(SettingsKey.nozzle_diameter))
{
var details = "{0} = {1}\n{2} = {3}".FormatWith(
GetSettingsName(SettingsKey.first_layer_height),
@ -81,13 +80,12 @@ namespace MatterHackers.MatterControl
settings.GetValue<double>(SettingsKey.nozzle_diameter));
errors.Add(
new ValidationError()
new SettingsValidationError(SettingsKey.first_layer_height)
{
Error = "{0} must be less than or equal to the {1}.".Localize().FormatWith(
GetSettingsName(SettingsKey.layer_height),
GetSettingsName(SettingsKey.nozzle_diameter)),
Details = details,
Location = GetSettingsLocation(SettingsKey.first_layer_height)
});
}
}
@ -102,22 +100,20 @@ namespace MatterHackers.MatterControl
if (startGCodeLine.StartsWith("G29"))
{
errors.Add(
new ValidationError()
new SettingsValidationError(SettingsKey.start_gcode)
{
Error = "Start G-Code cannot contain G29 if Print Recovery is enabled.".Localize(),
Details = "Your Start G-Code should not contain a G29 if you are planning on using Print Recovery. Change your start G-Code or turn off Print Recovery.".Localize(),
Location = GetSettingsLocation(SettingsKey.start_gcode)
});
}
if (startGCodeLine.StartsWith("G30"))
{
errors.Add(
new ValidationError()
new SettingsValidationError(SettingsKey.start_gcode)
{
Error = "Start G-Code cannot contain G30 if Print Leveling is enabled.".Localize(),
Details = "Your Start G-Code should not contain a G30 if you are planning on using Print Recovery. Change your start G-Code or turn off Print Recovery.".Localize(),
Location = GetSettingsLocation(SettingsKey.start_gcode)
});
}
}
@ -131,22 +127,20 @@ namespace MatterHackers.MatterControl
if (startGCodeLine.StartsWith("G29"))
{
errors.Add(
new ValidationError()
new SettingsValidationError(SettingsKey.start_gcode)
{
Error = "Start G-Code cannot contain G29 if Print Leveling is enabled.".Localize(),
Details = "Your Start G-Code should not contain a G29 if you are planning on using print leveling. Change your start G-Code or turn off print leveling.".Localize(),
Location = GetSettingsLocation(SettingsKey.start_gcode)
});
}
if (startGCodeLine.StartsWith("G30"))
{
errors.Add(
new ValidationError()
new SettingsValidationError(SettingsKey.start_gcode)
{
Error = "Start G-Code cannot contain G30 if Print Leveling is enabled.".Localize(),
Details = "Your Start G-Code should not contain a G30 if you are planning on using print leveling. Change your start G-Code or turn off print leveling.".Localize(),
Location = GetSettingsLocation(SettingsKey.start_gcode)
});
}
}
@ -165,8 +159,10 @@ namespace MatterHackers.MatterControl
new ValidationError()
{
Error = "Z Offset is too large.".Localize(),
Details = "The Z Offset for your printer, sometimes called Baby Stepping, is greater than 2mm and invalid. Clear the value and re-level the bed.".Localize(),
Location = location
Details = string.Format(
"{0}\n\n{1}",
"The Z Offset for your printer, sometimes called Baby Stepping, is greater than 2mm and invalid. Clear the value and re-level the bed.".Localize(),
location)
});
}
@ -179,27 +175,25 @@ namespace MatterHackers.MatterControl
settings.GetValue<double>(SettingsKey.nozzle_diameter));
errors.Add(
new ValidationError()
new SettingsValidationError(SettingsKey.first_layer_extrusion_width)
{
Error = "{0} must be less than or equal to the {1} * 4.".Localize().FormatWith(
GetSettingsName(SettingsKey.first_layer_extrusion_width),
GetSettingsName(SettingsKey.nozzle_diameter)),
Details = details,
Location = GetSettingsLocation(SettingsKey.first_layer_extrusion_width)
Details = details
});
}
if (settings.GetValue<double>(SettingsKey.first_layer_extrusion_width) <= 0)
{
errors.Add(
new ValidationError()
new SettingsValidationError(SettingsKey.first_layer_extrusion_width)
{
Error = "{0} must be greater than 0.".Localize().FormatWith(
GetSettingsName(SettingsKey.first_layer_extrusion_width)),
Details = "{0} = {1}".FormatWith(
GetSettingsName(SettingsKey.first_layer_extrusion_width),
settings.GetValue<double>(SettingsKey.first_layer_extrusion_width)),
Location = GetSettingsLocation(SettingsKey.first_layer_extrusion_width)
});
}
@ -212,13 +206,12 @@ namespace MatterHackers.MatterControl
settings.GetValue<double>(SettingsKey.nozzle_diameter));
errors.Add(
new ValidationError()
new SettingsValidationError(SettingsKey.external_perimeter_extrusion_width)
{
Error = "{0} must be less than or equal to the {1} * 4.".Localize().FormatWith(
GetSettingsName(SettingsKey.external_perimeter_extrusion_width),
GetSettingsName(SettingsKey.nozzle_diameter)),
Details = details,
Location = GetSettingsLocation(SettingsKey.external_perimeter_extrusion_width)
});
}
@ -226,66 +219,61 @@ namespace MatterHackers.MatterControl
{
errors.Add(
new ValidationError()
new SettingsValidationError(SettingsKey.external_perimeter_extrusion_width)
{
Error = "{0} must be greater than 0.".Localize().FormatWith(
GetSettingsName(SettingsKey.external_perimeter_extrusion_width)),
Details = "{0} = {1}".FormatWith(
GetSettingsName(SettingsKey.external_perimeter_extrusion_width),
settings.GetValue<double>(SettingsKey.external_perimeter_extrusion_width)),
Location = GetSettingsLocation(SettingsKey.external_perimeter_extrusion_width)
});
}
if (settings.GetValue<double>(SettingsKey.min_fan_speed) > 100)
{
errors.Add(
new ValidationError()
new SettingsValidationError(SettingsKey.min_fan_speed)
{
Error = "The {0} can only go as high as 100%.".Localize().FormatWith(
GetSettingsName(SettingsKey.min_fan_speed)),
Details = "It is currently set to {0}.".Localize().FormatWith(
settings.GetValue<double>(SettingsKey.min_fan_speed)),
Location = GetSettingsLocation(SettingsKey.min_fan_speed)
});
}
if (settings.GetValue<double>(SettingsKey.max_fan_speed) > 100)
{
errors.Add(
new ValidationError()
new SettingsValidationError(SettingsKey.max_fan_speed)
{
Error = "The {0} can only go as high as 100%.".Localize().FormatWith(
GetSettingsName(SettingsKey.max_fan_speed)),
Details = "It is currently set to {0}.".Localize().FormatWith(
settings.GetValue<double>(SettingsKey.max_fan_speed)),
Location = GetSettingsLocation(SettingsKey.max_fan_speed)
});
}
if (settings.GetValue<int>(SettingsKey.extruder_count) < 1)
{
errors.Add(
new ValidationError()
new SettingsValidationError(SettingsKey.extruder_count)
{
Error = "The {0} must be at least 1.".Localize().FormatWith(
GetSettingsName(SettingsKey.extruder_count)),
Details= "It is currently set to {0}.".Localize().FormatWith(
settings.GetValue<int>(SettingsKey.extruder_count)),
Location = GetSettingsLocation(SettingsKey.extruder_count)
});
}
if (settings.GetValue<double>(SettingsKey.fill_density) < 0 || settings.GetValue<double>(SettingsKey.fill_density) > 1)
{
errors.Add(
new ValidationError()
new SettingsValidationError(SettingsKey.fill_density)
{
Error = "The {0} must be between 0 and 1.".Localize().FormatWith(
GetSettingsName(SettingsKey.fill_density)),
Details = "It is currently set to {0}.".Localize().FormatWith(
settings.GetValue<double>(SettingsKey.fill_density)),
Location = GetSettingsLocation(SettingsKey.filament_density)
});
}
@ -299,19 +287,18 @@ namespace MatterHackers.MatterControl
ValidateGCodeLinesShortEnough(SettingsKey.start_gcode, printer, errors);
// If the given speed is part of the current slice engine then check that it is greater than 0.
ValidateGoodSpeedSettingGreaterThan0("bridge_speed", printer, errors);
ValidateGoodSpeedSettingGreaterThan0("air_gap_speed", printer, errors);
ValidateGoodSpeedSettingGreaterThan0("external_perimeter_speed", printer, errors);
ValidateGoodSpeedSettingGreaterThan0(SettingsKey.bridge_speed, printer, errors);
ValidateGoodSpeedSettingGreaterThan0(SettingsKey.air_gap_speed, printer, errors);
ValidateGoodSpeedSettingGreaterThan0(SettingsKey.external_perimeter_speed, printer, errors);
ValidateGoodSpeedSettingGreaterThan0(SettingsKey.first_layer_speed, printer, errors);
ValidateGoodSpeedSettingGreaterThan0("infill_speed", printer, errors);
ValidateGoodSpeedSettingGreaterThan0("perimeter_speed", printer, errors);
ValidateGoodSpeedSettingGreaterThan0("small_perimeter_speed", printer, errors);
ValidateGoodSpeedSettingGreaterThan0("solid_infill_speed", printer, errors);
ValidateGoodSpeedSettingGreaterThan0("support_material_speed", printer, errors);
ValidateGoodSpeedSettingGreaterThan0(SettingsKey.infill_speed, printer, errors);
ValidateGoodSpeedSettingGreaterThan0(SettingsKey.perimeter_speed, printer, errors);
ValidateGoodSpeedSettingGreaterThan0(SettingsKey.small_perimeter_speed, printer, errors);
ValidateGoodSpeedSettingGreaterThan0(SettingsKey.solid_infill_speed, printer, errors);
ValidateGoodSpeedSettingGreaterThan0(SettingsKey.support_material_speed, printer, errors);
ValidateGoodSpeedSettingGreaterThan0(SettingsKey.top_solid_infill_speed, printer, errors);
ValidateGoodSpeedSettingGreaterThan0("travel_speed", printer, errors);
ValidateGoodSpeedSettingGreaterThan0("retract_speed", printer, errors);
ValidateGoodSpeedSettingGreaterThan0(SettingsKey.travel_speed, printer, errors);
ValidateGoodSpeedSettingGreaterThan0(SettingsKey.retract_speed, printer, errors);
}
catch (Exception e)
{
@ -326,53 +313,30 @@ namespace MatterHackers.MatterControl
return errors;
}
private static string GetSettingsLocation(string settingsKey)
{
var settingData = SettingsOrganizer.Instance.GetSettingsData(settingsKey);
var setingsSectionName = settingData.OrganizerSubGroup.Group.Category.SettingsSection.Name;
var rootLevel = SettingsOrganizer.Instance.UserLevels[setingsSectionName];
var subGroup = rootLevel.GetContainerForSetting(settingsKey);
var category = subGroup.Group.Category;
if (setingsSectionName == "Advanced")
{
setingsSectionName = "Slice Settings";
}
return "Location".Localize() + ":"
+ "\n" + setingsSectionName.Localize()
+ "\n • " + category.Name.Localize()
+ "\n • " + subGroup.Group.Name.Localize()
+ "\n • " + settingData.PresentationName.Localize();
}
private static string GetSettingsName(string settingsKey)
{
var settingData = SettingsOrganizer.Instance.GetSettingsData(settingsKey);
return settingData.PresentationName.Localize();
}
private static bool ValidateGCodeLinesShortEnough(string gCodeSetting, PrinterConfig printer, List<ValidationError> errors)
private static bool ValidateGCodeLinesShortEnough(string settingsKey, PrinterConfig printer, List<ValidationError> errors)
{
string[] gCodeString = printer.Settings.GetValue(SettingsKey.start_gcode).Replace("\\n", "\n").Split('\n');
// make sure the custom gcode does not have lines too long to print
foreach (string line in gCodeString)
foreach (string line in printer.Settings.GetValue(settingsKey).Replace("\\n", "\n").Split('\n'))
{
var trimedLine = line.Split(';')[0].Trim();
var length = trimedLine.Length;
if (length > 100)
{
SliceSettingData data = SettingsOrganizer.Instance.GetSettingsData(gCodeSetting);
SliceSettingData data = SettingsOrganizer.Instance.GetSettingsData(settingsKey);
if (data != null)
{
var details = "Found a line that is {0} characters long.\n{1}...".Localize().FormatWith(length, trimedLine.Substring(0, 20));
errors.Add(
new ValidationError()
new SettingsValidationError(settingsKey)
{
Error = "All G-Code lines mush be shorter than 100 characters (excluding comments).".Localize().FormatWith(data.PresentationName),
Details = details,
Location = GetSettingsLocation(gCodeSetting)
});
}
@ -383,9 +347,9 @@ namespace MatterHackers.MatterControl
return true;
}
private static void ValidateGoodSpeedSettingGreaterThan0(string speedSetting, PrinterConfig printer, List<ValidationError> errors)
private static void ValidateGoodSpeedSettingGreaterThan0(string settingsKey, PrinterConfig printer, List<ValidationError> errors)
{
var actualSpeedValueString = printer.Settings.GetValue(speedSetting);
var actualSpeedValueString = printer.Settings.GetValue(settingsKey);
var speedValueString = actualSpeedValueString;
if (speedValueString.EndsWith("%"))
{
@ -400,18 +364,17 @@ namespace MatterHackers.MatterControl
}
if (!valueWasNumber
|| (printer.EngineMappingsMatterSlice.MapContains(speedSetting)
|| (printer.EngineMappingsMatterSlice.MapContains(settingsKey)
&& speedToCheck <= 0))
{
SliceSettingData data = SettingsOrganizer.Instance.GetSettingsData(speedSetting);
SliceSettingData data = SettingsOrganizer.Instance.GetSettingsData(settingsKey);
if (data != null)
{
errors.Add(
new ValidationError()
new SettingsValidationError(settingsKey)
{
Error = "The {0} must be greater than 0.".Localize().FormatWith(data.PresentationName),
Details = "It is currently set to {0}.".Localize().FormatWith(actualSpeedValueString),
Location = GetSettingsLocation(speedSetting)
});
}
}

View file

@ -0,0 +1,66 @@
/*
Copyright (c) 2019, John Lewin
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using MatterHackers.Localizations;
using MatterHackers.MatterControl.SlicerConfiguration;
namespace MatterHackers.MatterControl
{
public class SettingsValidationError : ValidationError
{
public SettingsValidationError(string settingsName)
{
this.CanonicalSettingsName = settingsName;
}
public string CanonicalSettingsName { get; }
public string Location => SettingsLocation(this.CanonicalSettingsName);
private static string SettingsLocation(string settingsKey)
{
var settingData = SettingsOrganizer.Instance.GetSettingsData(settingsKey);
var setingsSectionName = settingData.OrganizerSubGroup.Group.Category.SettingsSection.Name;
var rootLevel = SettingsOrganizer.Instance.UserLevels[setingsSectionName];
var subGroup = rootLevel.GetContainerForSetting(settingsKey);
var category = subGroup.Group.Category;
if (setingsSectionName == "Advanced")
{
setingsSectionName = "Slice Settings";
}
return "Location".Localize() + ":"
+ "\n" + setingsSectionName.Localize()
+ "\n • " + category.Name.Localize()
+ "\n • " + subGroup.Group.Name.Localize()
+ "\n • " + settingData.PresentationName.Localize();
}
}
}

View file

@ -27,6 +27,9 @@ of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
using MatterHackers.Localizations;
using MatterHackers.MatterControl.SlicerConfiguration;
namespace MatterHackers.MatterControl
{
public class ValidationError
@ -34,11 +37,5 @@ namespace MatterHackers.MatterControl
public string Error { get; set; }
public string Details { get; set; }
public string Source { get; set; }
public string SourceName { get; set; }
public string Location { get; set; }
}
}

View file

@ -1,5 +1,5 @@
/*
Copyright (c) 2018, Lars Brubaker, John Lewin
Copyright (c) 2019, Lars Brubaker, John Lewin
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -32,6 +32,8 @@ using System.Collections.Generic;
using System.Linq;
using Markdig.Agg;
using MatterHackers.Agg;
using MatterHackers.Agg.Image;
using MatterHackers.Agg.Platform;
using MatterHackers.Agg.UI;
using MatterHackers.Localizations;
using MatterHackers.MatterControl.CustomWidgets;
@ -42,6 +44,8 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
{
public class SliceSettingsRow : SettingsRow
{
private IEnumerable<SettingsValidationError> validationErrors;
private static Dictionary<string, Func<PrinterSettings, GuiWidget>> extendedInfo = new Dictionary<string, Func<PrinterSettings, GuiWidget>>()
{
#if DEBUG
@ -86,6 +90,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
private Popover popoverBubble = null;
private SystemWindow systemWindow = null;
private ValidationWrapper validationWrapper;
public SliceSettingsRow(PrinterConfig printer, SettingsContext settingsContext, SliceSettingData settingData, ThemeConfig theme, bool fullRowSelect = false)
: base (settingData.PresentationName.Localize(), settingData.HelpText.Localize(), theme, fullRowSelect: fullRowSelect)
@ -156,6 +161,23 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
this.PerformLayout();
}
public void UpdateValidationState(List<ValidationError> errors)
{
var fieldErrors = errors.OfType<SettingsValidationError>().Where(e => e.CanonicalSettingsName == this.settingData.SlicerConfigName);
if (fieldErrors.Any())
{
validationErrors = fieldErrors;
this.ContentValid = false;
}
else
{
validationErrors = Enumerable.Empty<SettingsValidationError>();
this.ContentValid = true;
}
}
public SettingsValidationError ValidationEror { get; set; }
public Color HighlightColor
{
get => overrideIndicator.BackgroundColor;
@ -168,6 +190,22 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
}
}
public bool ContentValid
{
get => validationWrapper?.ContentValid ?? true;
set
{
if (validationWrapper != null
&& validationWrapper.ContentValid != value)
{
validationWrapper.ContentValid = value;
// ShowPopever performs bounds validation and should have no effect if mouse is not in bounds
this.ShowPopover(this);
}
}
}
public override void OnClick(MouseEventArgs mouseEvent)
{
if (ActionWidget != null
@ -241,17 +279,20 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
public ArrowDirection ArrowDirection { get; set; } = ArrowDirection.Right;
public UIField UIField { get; internal set; }
public override void OnMouseEnterBounds(MouseEventArgs mouseEvent)
{
if (systemWindow == null)
{
return;
}
this.ShowPopover(this);
var settingsRow = this;
base.OnMouseEnterBounds(mouseEvent);
}
private void ShowPopover(SliceSettingsRow settingsRow)
{
// Only display popovers when we're the active widget, exit if we're not first under mouse
if (!this.ContainsFirstUnderMouseRecursive())
if (systemWindow == null
|| !this.ContainsFirstUnderMouseRecursive())
{
return;
}
@ -333,6 +374,33 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
}
}
if (validationErrors.Any())
{
var errorsPanel = new FlowLayoutWidget(FlowDirection.TopToBottom)
{
Padding = theme.DefaultContainerPadding / 2,
HAnchor = HAnchor.Stretch,
VAnchor = VAnchor.Fit
};
popover.AddChild(errorsPanel);
foreach (var item in validationErrors)
{
var errorPanel = new FlowLayoutWidget(FlowDirection.TopToBottom)
{
Margin = new BorderDouble(0, 5)
};
errorsPanel.AddChild(errorPanel);
errorsPanel.AddChild(
new WrappedTextWidget(item.Error, pointSize: theme.DefaultFontSize - 1, textColor: Color.Red)
{
Margin = new BorderDouble(bottom: 3)
});
errorsPanel.AddChild(new WrappedTextWidget(item.Details, pointSize: theme.DefaultFontSize - 1, textColor: Color.Red));
}
}
#if DEBUG
popover.AddChild(new TextWidget(settingData.SlicerConfigName + mapsTo, pointSize: theme.DefaultFontSize - 1, textColor: AppContext.Theme.TextColor)
{
@ -359,8 +427,6 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
secondsToClose: closeSeconds);
popoverBubble = popover;
base.OnMouseEnterBounds(mouseEvent);
}
public override void OnMouseLeaveBounds(MouseEventArgs mouseEvent)
@ -476,7 +542,59 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
public void AddContent(GuiWidget content)
{
dataArea.AddChild(content);
validationWrapper = new ValidationWrapper();
dataArea.AddChild(validationWrapper);
validationWrapper.AddChild(content);
}
/// <summary>
/// Wraps UIFields and conditionally displays validation error hints when validation errors occur
/// </summary>
private class ValidationWrapper : GuiWidget
{
private bool _contentValid = true;
private ImageBuffer exclamation;
public ValidationWrapper()
{
this.VAnchor = VAnchor.Fit;
this.HAnchor = HAnchor.Fit;
this.Padding = new BorderDouble(left: 5);
exclamation = AggContext.StaticData.LoadIcon("exclamation.png");
this.Border = new BorderDouble(bottom: 1);
}
public bool ContentValid
{
get => _contentValid;
set
{
if (_contentValid != value)
{
_contentValid = value;
this.Invalidate();
}
}
}
public override Color BorderColor
{
get => (this.ContentValid) ? base.BorderColor : Color.Red;
set => base.BorderColor = value;
}
public override void OnDraw(Graphics2D graphics2D)
{
base.OnDraw(graphics2D);
if (!this.ContentValid)
{
graphics2D.Render(exclamation, this.LocalBounds.Left, this.LocalBounds.Top - exclamation.Height);
}
}
}
}
}

View file

@ -208,6 +208,8 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
allUiFields = new Dictionary<string, UIField>();
var errors = printer.ValidateSettings();
// Loop over categories creating a tab for each
foreach (var category in userLevel.Categories)
{
@ -236,7 +238,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
this.CreateOemProfileInfoRow());
}
var groupSection = this.CreateGroupSection(group);
var groupSection = this.CreateGroupSection(group, errors);
groupSection.Name = group.Name + " Panel";
@ -376,7 +378,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
{ "Fan", "enable_fan" },
};
public SectionWidget CreateGroupSection(SettingsOrganizer.Group group)
public SectionWidget CreateGroupSection(SettingsOrganizer.Group group, List<ValidationError> errors)
{
var groupPanel = new FlowLayoutWidget(FlowDirection.TopToBottom)
{
@ -420,7 +422,7 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
if (printer.EngineMappingsMatterSlice.MapContains(settingData.SlicerConfigName)
&& settingShouldBeShown)
{
settingsRow = CreateItemRow(settingData);
settingsRow = CreateItemRow(settingData, errors);
if (firstRow)
{
@ -600,12 +602,12 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
return dataArea;
}
internal GuiWidget CreateItemRow(SliceSettingData settingData)
internal GuiWidget CreateItemRow(SliceSettingData settingData, List<ValidationError> errors)
{
return CreateItemRow(settingData, settingsContext, printer, theme, ref tabIndexForItem, allUiFields);
return CreateItemRow(settingData, settingsContext, printer, theme, ref tabIndexForItem, allUiFields, errors);
}
public static GuiWidget CreateItemRow(SliceSettingData settingData, SettingsContext settingsContext, PrinterConfig printer, ThemeConfig theme, ref int tabIndexForItem, Dictionary<string, UIField> fieldCache = null)
public static GuiWidget CreateItemRow(SliceSettingData settingData, SettingsContext settingsContext, PrinterConfig printer, ThemeConfig theme, ref int tabIndexForItem, Dictionary<string, UIField> fieldCache = null, List<ValidationError> errors = null)
{
string sliceSettingValue = settingsContext.GetValue(settingData.SlicerConfigName);
@ -819,6 +821,14 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
}
}
settingsRow.UIField = uiField;
uiField.Row = settingsRow;
if (errors?.Any() == true)
{
settingsRow.UpdateValidationState(errors);
}
// Invoke the UpdateStyle implementation
settingsRow.UpdateStyle();
@ -895,6 +905,8 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
private void Printer_SettingChanged(object s, StringEventArgs stringEvent)
{
var errors = printer.ValidateSettings();
if (stringEvent != null)
{
string settingsKey = stringEvent.Data;
@ -908,6 +920,8 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
currentValue,
userInitiated: false);
}
uifield.Row.UpdateValidationState(errors);
}
}
}

View file

@ -62,6 +62,8 @@ namespace MatterHackers.MatterControl.SlicerConfiguration
public string Name { get; set; }
public SliceSettingsRow Row { get; internal set; }
protected virtual string ConvertValue(string newValue)
{
return newValue;

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 B