Making the public property editor able to work with object rather than only IObject3D

This commit is contained in:
Lars Brubaker 2023-03-23 14:04:47 -07:00
parent 6163482a58
commit dfefb936ef
10 changed files with 106 additions and 80 deletions

View file

@ -36,7 +36,7 @@ namespace MatterHackers.MatterControl.DesignTools
{
public class PPEContext
{
public IObject3D item { get; set; }
public object item { get; set; }
public Dictionary<string, GuiWidget> editRows { get; private set; } = new Dictionary<string, GuiWidget>();
public GuiWidget GetEditRow(string propertyName)

View file

@ -56,16 +56,16 @@ namespace MatterHackers.MatterControl
{
this.libraryConfig = libraryConfig;
objectEditorsByType = new Dictionary<Type, HashSet<IObject3DEditor>>();
objectEditorsByType = new Dictionary<Type, HashSet<IObjectEditor>>();
}
private void MapTypesToEditor(IObject3DEditor editor)
private void MapTypesToEditor(IObjectEditor editor)
{
foreach (Type type in editor.SupportedTypes())
{
if (!objectEditorsByType.TryGetValue(type, out HashSet<IObject3DEditor> mappedEditors))
if (!objectEditorsByType.TryGetValue(type, out HashSet<IObjectEditor> mappedEditors))
{
mappedEditors = new HashSet<IObject3DEditor>();
mappedEditors = new HashSet<IObjectEditor>();
objectEditorsByType.Add(type, mappedEditors);
}
@ -73,16 +73,16 @@ namespace MatterHackers.MatterControl
}
}
public void Register(IObject3DEditor object3DEditor)
public void Register(IObjectEditor object3DEditor)
{
this.MapTypesToEditor(object3DEditor);
}
private Dictionary<Type, HashSet<IObject3DEditor>> objectEditorsByType;
private Dictionary<Type, HashSet<IObjectEditor>> objectEditorsByType;
public HashSet<IObject3DEditor> GetEditorsForType(Type selectedItemType)
public HashSet<IObjectEditor> GetEditorsForType(Type selectedItemType)
{
HashSet<IObject3DEditor> mappedEditors;
HashSet<IObjectEditor> mappedEditors;
objectEditorsByType.TryGetValue(selectedItemType, out mappedEditors);
if (mappedEditors == null)

View file

@ -59,7 +59,7 @@ namespace MatterHackers.MatterControl.DesignTools
this.controlLayer = controlLayer;
this.item = item;
var theme = ApplicationController.Instance.Theme;
WindowWidget = new WindowWidget(theme, new RectangleDouble(10, 10, 400, 200))
WindowWidget = new WindowWidget(theme, new RectangleDouble(10, 10, 650, 600))
{
BackgroundColor = theme.BackgroundColor.WithAlpha(200),
};

View file

@ -47,7 +47,7 @@ using Newtonsoft.Json;
namespace MatterHackers.MatterControl.Library
{
public class OpenSCADBuilder : IObject3DEditor
public class OpenSCADBuilder : IObjectEditor
{
private OpenScadObject3D item;
@ -84,7 +84,7 @@ namespace MatterHackers.MatterControl.Library
return dictionary;
}
public GuiWidget Create(IObject3D object3D, UndoBuffer undoBuffer, ThemeConfig theme)
public GuiWidget Create(object object3D, UndoBuffer undoBuffer, ThemeConfig theme)
{
this.item = object3D as OpenScadObject3D;

View file

@ -55,7 +55,7 @@ using MatterHackers.VectorMath;
namespace MatterHackers.MatterControl.DesignTools
{
public class PublicPropertyEditor : IObject3DEditor
public class PublicPropertyEditor : IObjectEditor
{
public string Name => "Property Editor";
@ -81,7 +81,7 @@ namespace MatterHackers.MatterControl.DesignTools
private SafeList<SettingsRow> rows = new SafeList<SettingsRow>();
public GuiWidget Create(IObject3D item, UndoBuffer undoBuffer, ThemeConfig theme)
public GuiWidget Create(object item, UndoBuffer undoBuffer, ThemeConfig theme)
{
var mainContainer = new FlowLayoutWidget(FlowDirection.TopToBottom)
{
@ -96,7 +96,10 @@ namespace MatterHackers.MatterControl.DesignTools
};
// CreateEditor
AddUnlockLinkIfRequired(context.item, mainContainer, theme);
if (context.item is IObject3D itemAsIObject3D)
{
AddUnlockLinkIfRequired(itemAsIObject3D, mainContainer, theme);
}
AddMarkDownDescription(context.item, mainContainer, theme);
@ -171,7 +174,10 @@ namespace MatterHackers.MatterControl.DesignTools
};
updateButton.Click += (s, e) =>
{
context.item.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties));
if (context.item is IObject3D itemAsIObject3D)
{
itemAsIObject3D.Invalidate(new InvalidateArgs(itemAsIObject3D, InvalidateType.Properties));
}
};
mainContainer.AddChild(updateButton);
}
@ -186,7 +192,7 @@ namespace MatterHackers.MatterControl.DesignTools
return mainContainer;
}
private void AddFunctionButtons(IObject3D item, FlowLayoutWidget mainContainer, ThemeConfig theme)
private void AddFunctionButtons(object item, FlowLayoutWidget mainContainer, ThemeConfig theme)
{
if (item is IEditorButtonProvider editorButtonProvider)
{
@ -279,7 +285,7 @@ namespace MatterHackers.MatterControl.DesignTools
return column;
}
public static IEnumerable<EditableProperty> GetEditablePropreties(IObject3D item)
public static IEnumerable<EditableProperty> GetEditablePropreties(object item)
{
return item.GetType().GetProperties(OwnedPropertiesOnly)
.Where(pi => (AllowedTypes.Contains(pi.PropertyType) || pi.PropertyType.IsEnum)
@ -307,9 +313,10 @@ namespace MatterHackers.MatterControl.DesignTools
return null;
}
var localItem = context.item;
var object3D = property.Item;
var propertyGridModifier = property.Item as IPropertyGridModifier;
var contextItem = context.item;
var contextObject3D = contextItem as IObject3D;
var propertyObject3D = property.Item as IObject3D;
var propertyGridModifier = property.Item as IPropertyGridModifier;
GuiWidget rowContainer = null;
@ -346,20 +353,20 @@ namespace MatterHackers.MatterControl.DesignTools
undoBuffer.AddAndDo(new UndoRedoActions(() =>
{
property.SetValue(valueFromString(oldValue));
object3D?.Invalidate(new InvalidateArgs(localItem, InvalidateType.Properties));
propertyObject3D?.Invalidate(new InvalidateArgs(contextObject3D, InvalidateType.Properties));
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
},
() =>
{
property.SetValue(valueFromString(newValue));
object3D?.Invalidate(new InvalidateArgs(localItem, InvalidateType.Properties));
propertyObject3D?.Invalidate(new InvalidateArgs(contextObject3D, InvalidateType.Properties));
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
}));
}
else
{
property.SetValue(valueFromString(newValue));
object3D?.Invalidate(new InvalidateArgs(localItem, InvalidateType.Properties));
propertyObject3D?.Invalidate(new InvalidateArgs(contextObject3D, InvalidateType.Properties));
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
}
};
@ -391,8 +398,8 @@ namespace MatterHackers.MatterControl.DesignTools
}
}
object3D.Invalidated += RefreshField;
valueField.Closed += (s, e) => object3D.Invalidated -= RefreshField;
propertyObject3D.Invalidated += RefreshField;
valueField.Closed += (s, e) => propertyObject3D.Invalidated -= RefreshField;
}
else // normal edit row
{
@ -414,11 +421,14 @@ namespace MatterHackers.MatterControl.DesignTools
}
}
object3D.Invalidated += RefreshField;
field.Content.Descendants<InternalTextEditWidget>().First().Name = property.DisplayName + " Edit";
field.Content.Closed += (s, e) => object3D.Invalidated -= RefreshField;
field.Content.Descendants<InternalTextEditWidget>().First().Name = property.DisplayName + " Edit";
if (propertyObject3D != null)
{
propertyObject3D.Invalidated += RefreshField;
field.Content.Closed += (s, e) => propertyObject3D.Invalidated -= RefreshField;
}
if (property.PropertyInfo.GetCustomAttributes(true).OfType<MaxDecimalPlacesAttribute>().FirstOrDefault() is MaxDecimalPlacesAttribute decimalPlaces)
if (property.PropertyInfo.GetCustomAttributes(true).OfType<MaxDecimalPlacesAttribute>().FirstOrDefault() is MaxDecimalPlacesAttribute decimalPlaces)
{
field.Content.Descendants<InternalNumberEdit>().First().MaxDecimalsPlaces = decimalPlaces.Number;
}
@ -430,12 +440,12 @@ namespace MatterHackers.MatterControl.DesignTools
}
else if (propertyValue is Color color)
{
var field = new ColorField(theme, object3D.Color, null, false);
var field = new ColorField(theme, color, null, false);
field.Initialize(0);
field.ValueChanged += (s, e) =>
{
property.SetValue(field.Color);
object3D?.Invalidate(new InvalidateArgs(localItem, InvalidateType.Properties));
propertyObject3D?.Invalidate(new InvalidateArgs(contextObject3D, InvalidateType.Properties));
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
};
@ -509,7 +519,7 @@ namespace MatterHackers.MatterControl.DesignTools
field.ValueChanged += (s, e) =>
{
property.SetValue(field.DirectionVector);
object3D?.Invalidate(new InvalidateArgs(localItem, InvalidateType.Properties));
propertyObject3D?.Invalidate(new InvalidateArgs(contextObject3D, InvalidateType.Properties));
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
};
@ -560,7 +570,7 @@ namespace MatterHackers.MatterControl.DesignTools
Normal = field1.DirectionVector.Normal,
Origin = property.Item.Children.First().GetAxisAlignedBoundingBox().Center + field2.Vector3
});
object3D?.Invalidate(new InvalidateArgs(localItem, InvalidateType.Properties));
propertyObject3D?.Invalidate(new InvalidateArgs(contextObject3D, InvalidateType.Properties));
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
};
field2.ValueChanged += (s, e) =>
@ -570,7 +580,7 @@ namespace MatterHackers.MatterControl.DesignTools
Normal = field1.DirectionVector.Normal,
Origin = property.Item.Children.First().GetAxisAlignedBoundingBox().Center + field2.Vector3
});
object3D?.Invalidate(new InvalidateArgs(localItem, InvalidateType.Properties));
propertyObject3D?.Invalidate(new InvalidateArgs(contextObject3D, InvalidateType.Properties));
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
};
@ -618,13 +628,13 @@ namespace MatterHackers.MatterControl.DesignTools
if (property.Item is OperationSourceContainerObject3D sourceContainer)
{
Action selected = null;
var showUpdate = localItem.GetType().GetCustomAttributes(typeof(ShowUpdateButtonAttribute), true).FirstOrDefault() as ShowUpdateButtonAttribute;
var showUpdate = contextItem.GetType().GetCustomAttributes(typeof(ShowUpdateButtonAttribute), true).FirstOrDefault() as ShowUpdateButtonAttribute;
if (showUpdate == null
|| !showUpdate.SuppressPropertyChangeUpdates)
{
selected = () =>
{
object3D?.Invalidate(new InvalidateArgs(localItem, InvalidateType.Properties));
propertyObject3D?.Invalidate(new InvalidateArgs(contextObject3D, InvalidateType.Properties));
};
}
@ -666,7 +676,7 @@ namespace MatterHackers.MatterControl.DesignTools
ImageBuffer GetImageCheckingForErrors()
{
var image = imageBuffer;
if (object3D is ImageObject3D imageObject2)
if (propertyObject3D is ImageObject3D imageObject2)
{
image = imageObject2.Image;
}
@ -705,10 +715,10 @@ namespace MatterHackers.MatterControl.DesignTools
}
}
object3D.Invalidated += RefreshField;
imageWidget.Closed += (s, e) => object3D.Invalidated -= RefreshField;
propertyObject3D.Invalidated += RefreshField;
imageWidget.Closed += (s, e) => propertyObject3D.Invalidated -= RefreshField;
if (object3D is IEditorWidgetModifier editorWidgetModifier)
if (propertyObject3D is IEditorWidgetModifier editorWidgetModifier)
{
editorWidgetModifier.ModifyEditorWidget(imageWidget, theme, UpdateEditorImage);
}
@ -724,15 +734,15 @@ namespace MatterHackers.MatterControl.DesignTools
{
if (e.InvalidateType.HasFlag(InvalidateType.DisplayValues))
{
if (object3D is IImageProvider imageProvider)
if (propertyObject3D is IImageProvider imageProvider)
{
var _ = imageProvider.Image;
}
}
}
object3D.Invalidated += RefreshField;
rowContainer.Closed += (s, e) => object3D.Invalidated -= RefreshField;
propertyObject3D.Invalidated += RefreshField;
rowContainer.Closed += (s, e) => propertyObject3D.Invalidated -= RefreshField;
}
else if (propertyValue is List<string> stringList)
{
@ -789,8 +799,8 @@ namespace MatterHackers.MatterControl.DesignTools
}
}
object3D.Invalidated += RefreshField;
valueField.Closed += (s, e) => object3D.Invalidated -= RefreshField;
propertyObject3D.Invalidated += RefreshField;
valueField.Closed += (s, e) => propertyObject3D.Invalidated -= RefreshField;
}
else // normal edit row
{
@ -813,8 +823,11 @@ namespace MatterHackers.MatterControl.DesignTools
}
}
object3D.Invalidated += RefreshField;
field.Content.Closed += (s, e) => object3D.Invalidated -= RefreshField;
if (propertyObject3D != null)
{
propertyObject3D.Invalidated += RefreshField;
field.Content.Closed += (s, e) => propertyObject3D.Invalidated -= RefreshField;
}
rowContainer = CreateSettingsRow(property, field.Content, theme);
}
@ -851,7 +864,7 @@ namespace MatterHackers.MatterControl.DesignTools
format = "0." + new string('#', Math.Min(10, decimalPlaces.Number));
}
field.SetValue(doubleExpresion.Value(object3D).ToString(format), false);
field.SetValue(doubleExpresion.Value(propertyObject3D).ToString(format), false);
}
field.ClearUndoHistory();
@ -896,15 +909,15 @@ namespace MatterHackers.MatterControl.DesignTools
format = "0." + new string('#', Math.Min(10, decimalPlaces.Number));
}
var rawValue = newValue.Value(object3D);
var rawValue = newValue.Value(propertyObject3D);
field.TextValue = rawValue.ToString(format);
}
}
}
}
object3D.Invalidated += RefreshField;
field.Content.Closed += (s, e) => object3D.Invalidated -= RefreshField;
propertyObject3D.Invalidated += RefreshField;
field.Content.Closed += (s, e) => propertyObject3D.Invalidated -= RefreshField;
}
else if (propertyValue is IntOrExpression intExpresion)
{
@ -926,7 +939,7 @@ namespace MatterHackers.MatterControl.DesignTools
format = "0." + new string('#', Math.Min(10, decimalPlaces.Number));
}
field.SetValue(intExpresion.Value(object3D).ToString(format), false);
field.SetValue(intExpresion.Value(propertyObject3D).ToString(format), false);
}
field.ClearUndoHistory();
@ -971,15 +984,15 @@ namespace MatterHackers.MatterControl.DesignTools
format = "0." + new string('#', Math.Min(10, decimalPlaces.Number));
}
var rawValue = newValue.Value(object3D);
var rawValue = newValue.Value(propertyObject3D);
field.TextValue = rawValue.ToString(format);
}
}
}
}
object3D.Invalidated += RefreshField;
field.Content.Closed += (s, e) => object3D.Invalidated -= RefreshField;
propertyObject3D.Invalidated += RefreshField;
field.Content.Closed += (s, e) => propertyObject3D.Invalidated -= RefreshField;
}
else if (propertyValue is string stringValue)
{
@ -987,7 +1000,7 @@ namespace MatterHackers.MatterControl.DesignTools
{
rowContainer = NewImageSearchWidget(theme);
}
else if (object3D is AssetObject3D assetObject
else if (propertyObject3D is AssetObject3D assetObject
&& property.PropertyInfo.Name == "AssetPath")
{
// This is the AssetPath property of an asset object, add a button to set the AssetPath from a file
@ -1065,8 +1078,8 @@ namespace MatterHackers.MatterControl.DesignTools
}
}
object3D.Invalidated += RefreshField;
wrappedTextWidget.Closed += (s, e) => object3D.Invalidated -= RefreshField;
propertyObject3D.Invalidated += RefreshField;
wrappedTextWidget.Closed += (s, e) => propertyObject3D.Invalidated -= RefreshField;
}
else // normal edit row
{
@ -1181,7 +1194,7 @@ namespace MatterHackers.MatterControl.DesignTools
field.ValueChanged += (s, e) =>
{
property.SetValue(Convert.ToChar(field.Value));
object3D?.Invalidate(new InvalidateArgs(localItem, InvalidateType.Properties));
propertyObject3D?.Invalidate(new InvalidateArgs(contextObject3D, InvalidateType.Properties));
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
};
@ -1229,7 +1242,7 @@ namespace MatterHackers.MatterControl.DesignTools
if (property.Value.ToString() != field.Value)
{
property.SetValue(Enum.Parse(property.PropertyType, field.Value));
object3D?.Invalidate(new InvalidateArgs(localItem, InvalidateType.Properties));
propertyObject3D?.Invalidate(new InvalidateArgs(contextObject3D, InvalidateType.Properties));
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
}
};
@ -1261,15 +1274,15 @@ namespace MatterHackers.MatterControl.DesignTools
}
}
if (object3D != null)
if (propertyObject3D != null)
{
object3D.Invalidated += RefreshField;
field.Content.Closed += (s, e) => object3D.Invalidated -= RefreshField;
propertyObject3D.Invalidated += RefreshField;
field.Content.Closed += (s, e) => propertyObject3D.Invalidated -= RefreshField;
}
}
else if (propertyValue is IObject3D item
&& ApplicationController.Instance.Extensions.GetEditorsForType(property.PropertyType)?.FirstOrDefault() is IObject3DEditor iObject3DEditor)
&& ApplicationController.Instance.Extensions.GetEditorsForType(property.PropertyType)?.FirstOrDefault() is IObjectEditor iObject3DEditor)
{
// Use known IObject3D editors
rowContainer = iObject3DEditor.Create(item, undoBuffer, theme);
@ -1523,7 +1536,7 @@ namespace MatterHackers.MatterControl.DesignTools
}
}
public static void AddMarkDownDescription(IObject3D item, GuiWidget editControlsContainer, ThemeConfig theme)
public static void AddMarkDownDescription(object item, GuiWidget editControlsContainer, ThemeConfig theme)
{
if (item.GetType().GetCustomAttributes(typeof(MarkDownDescriptionAttribute), true).FirstOrDefault() is MarkDownDescriptionAttribute markdownDescription)
{
@ -1554,7 +1567,7 @@ namespace MatterHackers.MatterControl.DesignTools
return new SettingsRow("Demo Mode".Localize(), null, detailsLink, theme);
}
public static void AddWebPageLinkIfRequired(IObject3D item, FlowLayoutWidget editControlsContainer, ThemeConfig theme)
public static void AddWebPageLinkIfRequired(object item, FlowLayoutWidget editControlsContainer, ThemeConfig theme)
{
if (item.GetType().GetCustomAttributes(typeof(WebPageLinkAttribute), true).FirstOrDefault() is WebPageLinkAttribute unlockLink)
{

View file

@ -228,14 +228,15 @@ namespace MatterHackers.MatterControl.DesignTools
private static void SetValue(EditableProperty property, PPEContext context, Func<string, object> valueFromString, double sliderDownValue)
{
var localItem = context.item;
var object3D = property.Item;
var localObject3D = localItem as Object3D;
var object3D = property.Item;
var propertyGridModifier = property.Item as IPropertyGridModifier;
property.SetValue(valueFromString(sliderDownValue.ToString()));
object3D?.Invalidate(new InvalidateArgs(localItem, InvalidateType.Properties));
object3D?.Invalidate(new InvalidateArgs(localObject3D, InvalidateType.Properties));
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
object3D?.Invalidate(new InvalidateArgs(localItem, InvalidateType.DisplayValues));
object3D?.Invalidate(new InvalidateArgs(localObject3D, InvalidateType.DisplayValues));
}
}
}

View file

@ -250,7 +250,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
editorSectionWidget.Text = selectedItem.Name ?? selectedItemType.Name;
HashSet<IObject3DEditor> mappedEditors = ApplicationController.Instance.Extensions.GetEditorsForType(selectedItemType);
HashSet<IObjectEditor> mappedEditors = ApplicationController.Instance.Extensions.GetEditorsForType(selectedItemType);
var undoBuffer = sceneContext.Scene.UndoBuffer;
@ -310,7 +310,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
else
{
if (item != null
&& ApplicationController.Instance.Extensions.GetEditorsForType(item.GetType())?.FirstOrDefault() is IObject3DEditor editor)
&& ApplicationController.Instance.Extensions.GetEditorsForType(item.GetType())?.FirstOrDefault() is IObjectEditor editor)
{
ShowObjectEditor((editor, item, item.Name), selectedItem);
}
@ -559,7 +559,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
{
if (instance is IObject3D object3D)
{
if (ApplicationController.Instance.Extensions.GetEditorsForType(object3D.GetType())?.FirstOrDefault() is IObject3DEditor editor)
if (ApplicationController.Instance.Extensions.GetEditorsForType(object3D.GetType())?.FirstOrDefault() is IObjectEditor editor)
{
ShowObjectEditor((editor, object3D, object3D.Name), selectedItem);
}
@ -668,7 +668,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow
}
}
private void ShowObjectEditor((IObject3DEditor editor, IObject3D item, string displayName) scopeItem, IObject3D rootSelection)
private void ShowObjectEditor((IObjectEditor editor, IObject3D item, string displayName) scopeItem, IObject3D rootSelection)
{
var selectedItem = scopeItem.item;

View file

@ -237,13 +237,13 @@ namespace MatterHackers.MatterControl.DesignTools
}
}
public class SheetEditor : IObject3DEditor
public class SheetEditor : IObjectEditor
{
string IObject3DEditor.Name => "Sheet Editor";
string IObjectEditor.Name => "Sheet Editor";
IEnumerable<Type> IObject3DEditor.SupportedTypes() => new[] { typeof(SheetObject3D) };
IEnumerable<Type> IObjectEditor.SupportedTypes() => new[] { typeof(SheetObject3D) };
public GuiWidget Create(IObject3D item, UndoBuffer undoBuffer, ThemeConfig theme)
public GuiWidget Create(object item, UndoBuffer undoBuffer, ThemeConfig theme)
{
if (item is SheetObject3D sheetObject)
{

View file

@ -35,10 +35,10 @@ using System.Collections.Generic;
namespace MatterHackers.MatterControl.PartPreviewWindow
{
public interface IObject3DEditor
public interface IObjectEditor
{
string Name { get; }
IEnumerable<Type> SupportedTypes();
GuiWidget Create(IObject3D item, UndoBuffer undoBuffer, ThemeConfig theme);
GuiWidget Create(object item, UndoBuffer undoBuffer, ThemeConfig theme);
}
}

View file

@ -511,6 +511,9 @@ Translated:Bad
English:Bad Thermistor
Translated:Bad Thermistor
English:Bambu Studio
Translated:Bambu Studio
English:Base
Translated:Base
@ -2401,6 +2404,9 @@ Translated:Infill Angle
English:Infill Overlap
Translated:Infill Overlap
English:Infill Percent
Translated:Infill Percent
English:Infill Speeds
Translated:Infill Speeds
@ -2569,6 +2575,9 @@ Translated:Layer
English:Layer Change G-Code
Translated:Layer Change G-Code
English:Layer Height
Translated:Layer Height
English:Layer Shift
Translated:Layer Shift
@ -3406,6 +3415,9 @@ Translated:Open Recent
English:Open Settings View Options
Translated:Open Settings View Options
English:Open With
Translated:Open With
English:OpenSCAD not installed
Translated:OpenSCAD not installed