mattercontrol/MatterControlLib/DesignTools/PublicPropertyEditor.cs

862 lines
27 KiB
C#
Raw Normal View History

/*
Copyright (c) 2018, Lars Brubaker, 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 System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Threading;
using MatterHackers.Agg;
using MatterHackers.Agg.Image;
using MatterHackers.Agg.Platform;
using MatterHackers.Agg.UI;
using MatterHackers.DataConverters3D;
using MatterHackers.Localizations;
using MatterHackers.MatterControl.CustomWidgets;
using MatterHackers.MatterControl.DesignTools.EditableTypes;
2019-02-11 18:16:05 -08:00
using MatterHackers.MatterControl.DesignTools.Operations;
using MatterHackers.MatterControl.PartPreviewWindow;
using MatterHackers.MatterControl.PartPreviewWindow.View3D;
using MatterHackers.MatterControl.SlicerConfiguration;
using MatterHackers.VectorMath;
namespace MatterHackers.MatterControl.DesignTools
{
public class EditableProperty
{
public IObject3D Item { get; private set; }
2018-06-08 08:41:15 -07:00
2019-04-22 09:23:56 -07:00
public object Source { get; private set; }
public PropertyInfo PropertyInfo { get; private set; }
2018-06-09 07:38:27 -07:00
2018-06-08 08:41:15 -07:00
public EditableProperty(PropertyInfo p, object source)
{
2019-04-22 09:23:56 -07:00
this.Source = source;
2018-06-08 08:41:15 -07:00
this.Item = source as IObject3D;
this.PropertyInfo = p;
}
private string GetDescription(PropertyInfo prop)
{
var nameAttribute = prop.GetCustomAttributes(true).OfType<DescriptionAttribute>().FirstOrDefault();
return nameAttribute?.Description ?? null;
}
public static string GetDisplayName(PropertyInfo prop)
{
var nameAttribute = prop.GetCustomAttributes(true).OfType<DisplayNameAttribute>().FirstOrDefault();
return nameAttribute?.DisplayName ?? prop.Name.SplitCamelCase();
}
2019-04-22 09:23:56 -07:00
public object Value => PropertyInfo.GetGetMethod().Invoke(Source, null);
2018-06-09 07:38:27 -07:00
/// <summary>
2019-04-22 09:23:56 -07:00
/// Use reflection to set property value.
2018-06-09 07:38:27 -07:00
/// </summary>
2019-04-22 09:23:56 -07:00
/// <param name="value">The value to set through reflection.</param>
2018-06-09 07:38:27 -07:00
public void SetValue(object value)
{
2019-04-22 09:23:56 -07:00
this.PropertyInfo.GetSetMethod().Invoke(Source, new object[] { value });
2018-06-09 07:38:27 -07:00
}
public string DisplayName => GetDisplayName(PropertyInfo);
2019-04-22 09:23:56 -07:00
public string Description => GetDescription(PropertyInfo);
2019-04-22 09:23:56 -07:00
public Type PropertyType => PropertyInfo.PropertyType;
}
2018-01-31 13:15:29 -08:00
public class PublicPropertyEditor : IObject3DEditor
{
public string Name => "Property Editor";
public bool Unlocked { get; } = true;
2018-06-20 08:09:35 -07:00
public IEnumerable<Type> SupportedTypes() => new Type[] { typeof(IObject3D) };
2018-03-27 14:03:12 -07:00
2019-04-22 09:23:56 -07:00
private static readonly Type[] allowedTypes =
2018-01-28 07:53:23 -08:00
{
typeof(double), typeof(int), typeof(char), typeof(string), typeof(bool),
typeof(Color),
typeof(Vector2), typeof(Vector3),
typeof(DirectionVector), typeof(DirectionAxis),
typeof(SelectedChildren),
typeof(ImageBuffer),
typeof(List<string>)
2018-01-28 07:53:23 -08:00
};
2018-01-31 14:34:10 -08:00
public const BindingFlags OwnedPropertiesOnly = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
2018-01-28 07:53:23 -08:00
public GuiWidget Create(IObject3D item, UndoBuffer undoBuffer, ThemeConfig theme)
{
var mainContainer = new FlowLayoutWidget(FlowDirection.TopToBottom)
{
HAnchor = HAnchor.Stretch
};
if (item != null)
{
var context = new PPEContext()
{
item = item
};
// CreateEditor
2018-06-09 09:01:24 -07:00
AddUnlockLinkIfRequired(context, mainContainer, theme);
GuiWidget scope = mainContainer;
2018-06-09 09:01:24 -07:00
// Create a field editor for each editable property detected via reflection
foreach (var property in GetEditablePropreties(context.item))
{
if (property.PropertyInfo.GetCustomAttributes(true).OfType<HideFromEditorAttribute>().Any())
{
continue;
}
// Create SectionWidget for SectionStartAttributes
if (property.PropertyInfo.GetCustomAttributes(true).OfType<SectionStartAttribute>().FirstOrDefault() is SectionStartAttribute sectionStart)
{
var column = new FlowLayoutWidget()
{
FlowDirection = FlowDirection.TopToBottom,
Padding = new BorderDouble(theme.DefaultContainerPadding).Clone(top: 0)
};
var section = new SectionWidget(sectionStart.Title, column, theme);
theme.ApplyBoxStyle(section);
mainContainer.AddChild(section);
scope = column;
}
// Create SectionWidget for SectionStartAttributes
if (property.PropertyInfo.GetCustomAttributes(true).OfType<SectionEndAttribute>().Any())
{
// Push scope back to mainContainer on
scope = mainContainer;
}
2018-06-09 10:28:06 -07:00
var editor = CreatePropertyEditor(property, undoBuffer, context, theme);
if (editor != null)
{
scope.AddChild(editor);
}
2018-06-09 09:01:24 -07:00
}
AddWebPageLinkIfRequired(context, mainContainer, theme);
2018-06-09 09:01:24 -07:00
// add in an Update button if applicable
2019-04-22 09:23:56 -07:00
if (context.item.GetType().GetCustomAttributes(typeof(ShowUpdateButtonAttribute), true).FirstOrDefault() is ShowUpdateButtonAttribute showUpdate)
2018-06-09 09:01:24 -07:00
{
2018-06-25 08:40:09 -07:00
var updateButton = new TextButton("Update".Localize(), theme)
{
Margin = 5,
BackgroundColor = theme.MinimalShade,
HAnchor = HAnchor.Right,
VAnchor = VAnchor.Absolute
};
2018-06-09 09:01:24 -07:00
updateButton.Click += (s, e) =>
{
context.item.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties));
2018-06-09 09:01:24 -07:00
};
mainContainer.AddChild(updateButton);
}
// Init with custom 'UpdateControls' hooks
(context.item as IPropertyGridModifier)?.UpdateControls(new PublicPropertyChange(context, "Update_Button"));
}
return mainContainer;
}
private static FlowLayoutWidget CreateSettingsRow(EditableProperty property, UIField field, ThemeConfig theme = null)
{
var row = CreateSettingsRow(property.DisplayName.Localize(), property.Description.Localize(), theme);
row.AddChild(field.Content);
2018-06-08 16:20:57 -07:00
return row;
}
public static FlowLayoutWidget CreateSettingsRow(string labelText, string toolTipText = null, ThemeConfig theme = null)
{
if (theme == null)
{
theme = AppContext.Theme;
}
var rowContainer = new FlowLayoutWidget(FlowDirection.LeftToRight)
{
HAnchor = HAnchor.Stretch,
Padding = new BorderDouble(5),
ToolTipText = toolTipText
};
rowContainer.AddChild(new TextWidget(labelText + ":", pointSize: 11, textColor: theme.TextColor)
{
Margin = new BorderDouble(0, 0, 3, 0),
VAnchor = VAnchor.Center,
});
rowContainer.AddChild(new HorizontalSpacer());
return rowContainer;
}
2018-07-02 14:50:51 -07:00
private static FlowLayoutWidget CreateSettingsColumn(EditableProperty property, UIField field)
{
2018-07-02 15:09:02 -07:00
return CreateSettingsColumn(property.DisplayName.Localize(), field, property.Description.Localize());
2018-07-02 14:50:51 -07:00
}
private static FlowLayoutWidget CreateSettingsColumn(EditableProperty property)
{
return CreateSettingsColumn(property.DisplayName.Localize(), property.Description.Localize());
}
2018-07-02 15:09:02 -07:00
private static FlowLayoutWidget CreateSettingsColumn(string labelText, UIField field, string toolTipText = null)
{
var column = CreateSettingsColumn(labelText, toolTipText);
var row = new FlowLayoutWidget()
{
HAnchor = HAnchor.Stretch
};
row.AddChild(new HorizontalSpacer());
row.AddChild(field.Content);
column.AddChild(row);
return column;
}
private static FlowLayoutWidget CreateSettingsColumn(string labelText, string toolTipText = null)
{
var columnContainer = new FlowLayoutWidget(FlowDirection.TopToBottom)
{
HAnchor = HAnchor.Stretch,
Padding = new BorderDouble(5),
ToolTipText = toolTipText
};
var label = new TextWidget(labelText + ":", pointSize: 11, textColor: AppContext.Theme.TextColor)
{
Margin = new BorderDouble(0, 3, 0, 0),
HAnchor = HAnchor.Left
};
columnContainer.AddChild(label);
return columnContainer;
}
public static IEnumerable<EditableProperty> GetEditablePropreties(IObject3D item)
2018-04-01 16:06:31 -07:00
{
return item.GetType().GetProperties(OwnedPropertiesOnly)
.Where(pi => (allowedTypes.Contains(pi.PropertyType) || pi.PropertyType.IsEnum)
&& pi.GetGetMethod() != null
&& pi.GetSetMethod() != null)
.Select(p => new EditableProperty(p, item));
}
2018-06-09 10:28:06 -07:00
public static GuiWidget CreatePropertyEditor(EditableProperty property, UndoBuffer undoBuffer, PPEContext context, ThemeConfig theme)
{
var object3D = property.Item;
2018-06-09 08:56:16 -07:00
var propertyGridModifier = property.Item as IPropertyGridModifier;
GuiWidget rowContainer = null;
// Get reflected property value once, then test for each case below
var propertyValue = property.Value;
void RegisterValueChanged(UIField field, Func<string, object> valueFromString, Func<object, string> valueToString = null)
{
field.ValueChanged += (s, e) =>
2018-01-21 14:38:36 -08:00
{
var newValue = field.Value;
var oldValue = property.Value.ToString();
if (valueToString != null)
{
oldValue = valueToString(property.Value);
}
2018-10-20 12:49:28 -07:00
2019-04-22 09:23:56 -07:00
// field.Content
if (undoBuffer != null)
2018-10-20 12:49:28 -07:00
{
undoBuffer.AddAndDo(new UndoRedoActions(() =>
{
property.SetValue(valueFromString(oldValue));
object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties));
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
},
() =>
{
property.SetValue(valueFromString(newValue));
object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties));
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
}));
}
else
2018-10-20 12:49:28 -07:00
{
property.SetValue(valueFromString(newValue));
object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties));
2018-10-20 12:49:28 -07:00
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
}
};
}
// create a double editor
if (propertyValue is double doubleValue)
{
var field = new DoubleField(theme);
field.Initialize(0);
field.DoubleValue = doubleValue;
RegisterValueChanged(field, (valueString) => { return double.Parse(valueString); });
void RefreshField(object s, InvalidateArgs e)
{
if (e.InvalidateType.HasFlag(InvalidateType.DisplayValues))
{
double newValue = (double)property.Value;
if (newValue != field.DoubleValue)
{
field.DoubleValue = newValue;
}
}
}
object3D.Invalidated += RefreshField;
field.Content.Closed += (s, e) => object3D.Invalidated -= RefreshField;
rowContainer = CreateSettingsRow(property, field);
}
else if (propertyValue is Color color)
{
var field = new ColorField(theme, object3D.Color);
field.Initialize(0);
field.ValueChanged += (s, e) =>
{
property.SetValue(field.Color);
object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties));
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
};
rowContainer = CreateSettingsRow(property, field);
}
else if (propertyValue is Vector2 vector2)
{
var field = new Vector2Field(theme);
field.Initialize(0);
field.Vector2 = vector2;
RegisterValueChanged(field,
2019-04-03 15:03:45 -07:00
(valueString) => Vector2.Parse(valueString),
(value) =>
{
var s = ((Vector2)value).ToString();
return s.Substring(1, s.Length - 2);
});
2018-07-02 14:50:51 -07:00
rowContainer = CreateSettingsColumn(property, field);
}
else if (propertyValue is Vector3 vector3)
{
var field = new Vector3Field(theme);
field.Initialize(0);
field.Vector3 = vector3;
2019-04-03 15:03:45 -07:00
RegisterValueChanged(
field,
(valueString) => Vector3.Parse(valueString),
(value) =>
{
var s = ((Vector3)value).ToString();
return s.Substring(1, s.Length - 2);
});
2019-04-03 15:03:45 -07:00
2018-07-02 14:50:51 -07:00
rowContainer = CreateSettingsColumn(property, field);
}
else if (propertyValue is DirectionVector directionVector)
{
2018-07-12 09:22:28 -07:00
var field = new DirectionVectorField(theme);
field.Initialize(0);
field.SetValue(directionVector);
field.ValueChanged += (s, e) =>
{
2018-06-09 07:38:27 -07:00
property.SetValue(field.DirectionVector);
object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties));
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
2018-06-08 16:25:35 -07:00
};
rowContainer = CreateSettingsRow(property, field);
}
else if (propertyValue is DirectionAxis directionAxis)
{
rowContainer = CreateSettingsColumn(property);
var newDirectionVector = new DirectionVector()
{
Normal = directionAxis.Normal
};
var row1 = CreateSettingsRow("Axis".Localize());
2018-07-12 09:22:28 -07:00
var field1 = new DirectionVectorField(theme);
field1.Initialize(0);
field1.SetValue(newDirectionVector);
row1.AddChild(field1.Content);
rowContainer.AddChild(row1);
2018-06-08 16:25:35 -07:00
// the direction axis
// the distance from the center of the part
// create a double editor
var field2 = new Vector3Field(theme);
field2.Initialize(0);
2018-07-02 14:50:51 -07:00
field2.Vector3 = directionAxis.Origin - property.Item.Children.First().GetAxisAlignedBoundingBox().Center;
2018-07-02 15:09:02 -07:00
var row2 = CreateSettingsColumn("Offset", field2);
2018-06-08 16:20:57 -07:00
2018-06-08 21:08:56 -07:00
// update this when changed
2019-04-22 09:23:56 -07:00
void UpdateData(object s, InvalidateArgs e)
{
2018-07-02 14:50:51 -07:00
field2.Vector3 = ((DirectionAxis)property.Value).Origin - property.Item.Children.First().GetAxisAlignedBoundingBox().Center;
2019-04-22 09:23:56 -07:00
}
property.Item.Invalidated += UpdateData;
field2.Content.Closed += (s, e) =>
2018-06-08 16:25:35 -07:00
{
2019-04-22 09:23:56 -07:00
property.Item.Invalidated -= UpdateData;
2018-06-08 16:25:35 -07:00
};
// update functions
field1.ValueChanged += (s, e) =>
{
property.SetValue(new DirectionAxis()
{
Normal = field1.DirectionVector.Normal,
2018-07-02 14:50:51 -07:00
Origin = property.Item.Children.First().GetAxisAlignedBoundingBox().Center + field2.Vector3
});
object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties));
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
};
field2.ValueChanged += (s, e) =>
{
property.SetValue(new DirectionAxis()
{
Normal = field1.DirectionVector.Normal,
2018-07-02 14:50:51 -07:00
Origin = property.Item.Children.First().GetAxisAlignedBoundingBox().Center + field2.Vector3
});
object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties));
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
};
rowContainer.AddChild(row2);
}
else if (propertyValue is SelectedChildren childSelector)
{
var showAsList = property.PropertyInfo.GetCustomAttributes(true).OfType<ShowAsListAttribute>().FirstOrDefault() != null;
if (showAsList)
{
UIField field = new ChildrenSelectorListField(property, theme);
field.Initialize(0);
RegisterValueChanged(field,
(valueString) =>
{
var childrenSelector = new SelectedChildren();
foreach (var child in valueString.Split(','))
{
childrenSelector.Add(child);
}
2019-04-22 09:23:56 -07:00
return childrenSelector;
});
rowContainer = CreateSettingsRow(property, field);
}
2018-11-29 13:41:24 -08:00
else // show the subtract editor for boolean subtract and subtract and replace
{
rowContainer = CreateSettingsColumn(property);
2019-02-11 18:16:05 -08:00
if (property.Item is OperationSourceContainerObject3D sourceContainer)
{
rowContainer.AddChild(CreateSourceChildSelector(childSelector, sourceContainer, theme));
}
else
{
rowContainer.AddChild(CreateSelector(childSelector, property.Item, theme));
}
}
}
else if (propertyValue is ImageBuffer imageBuffer)
{
rowContainer = CreateSettingsColumn(property);
2019-04-22 09:23:56 -07:00
rowContainer.AddChild(new ImageWidget(imageBuffer));
}
#if !__ANDROID__
else if (propertyValue is List<string> stringList)
{
2019-05-21 16:04:48 -07:00
var field = new SurfacedEditorsField(theme, property.Item);
field.Initialize(0);
field.ListValue = stringList;
field.ValueChanged += (s, e) =>
{
property.SetValue(field.ListValue);
};
rowContainer = CreateSettingsColumn(property, field);
rowContainer.Descendants<HorizontalSpacer>().FirstOrDefault()?.Close();
}
#endif
// create a int editor
else if (propertyValue is int intValue)
{
var field = new IntField(theme);
field.Initialize(0);
field.IntValue = intValue;
RegisterValueChanged(field, (valueString) => { return int.Parse(valueString); });
void RefreshField(object s, InvalidateArgs e)
{
if (e.InvalidateType.HasFlag(InvalidateType.DisplayValues))
{
int newValue = (int)property.Value;
if (newValue != field.IntValue)
{
field.IntValue = newValue;
}
}
}
object3D.Invalidated += RefreshField;
field.Content.Closed += (s, e) => object3D.Invalidated -= RefreshField;
rowContainer = CreateSettingsRow(property, field);
}
else if (propertyValue is bool boolValue)
{
2019-04-22 09:23:56 -07:00
// create a bool editor
2018-04-12 17:45:50 -07:00
var field = new ToggleboxField(theme);
field.Initialize(0);
field.Checked = boolValue;
RegisterValueChanged(field,
(valueString) => { return valueString == "1"; },
2019-04-22 09:23:56 -07:00
(value) => { return ((bool)value) ? "1" : "0"; });
rowContainer = CreateSettingsRow(property, field);
}
else if (propertyValue is string stringValue)
{
2019-04-22 09:23:56 -07:00
// create a string editor
var field = new TextField(theme);
2018-06-08 16:48:50 -07:00
field.Initialize(0);
field.SetValue(stringValue, false);
field.Content.HAnchor = HAnchor.Stretch;
RegisterValueChanged(field, (valueString) => valueString);
rowContainer = CreateSettingsRow(property, field);
var label = rowContainer.Children.First();
if (field is TextField)
{
var spacer = rowContainer.Children.OfType<HorizontalSpacer>().FirstOrDefault();
spacer.HAnchor = HAnchor.Absolute;
spacer.Width = Math.Max(0, 100 - label.Width);
}
}
else if (propertyValue is char charValue)
{
2019-04-22 09:23:56 -07:00
// create a char editor
var field = new CharField(theme);
2018-06-08 18:19:06 -07:00
field.Initialize(0);
field.SetValue(charValue.ToString(), false);
field.ValueChanged += (s, e) =>
{
2018-06-09 07:38:27 -07:00
property.SetValue(Convert.ToChar(field.Value));
object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties));
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
};
2018-06-08 18:19:06 -07:00
rowContainer = CreateSettingsRow(property, field);
}
else if (property.PropertyType.IsEnum)
{
2019-04-22 09:23:56 -07:00
// create an enum editor
2018-06-09 07:14:38 -07:00
UIField field;
var iconsAttribute = property.PropertyInfo.GetCustomAttributes(true).OfType<IconsAttribute>().FirstOrDefault();
2018-06-09 07:14:38 -07:00
if (iconsAttribute != null)
{
2018-07-12 09:22:28 -07:00
field = new IconEnumField(property, iconsAttribute, theme)
{
InitialValue = propertyValue.ToString()
};
2018-06-09 07:14:38 -07:00
}
else
{
2018-07-12 09:22:28 -07:00
field = new EnumField(property, theme);
2018-06-09 07:14:38 -07:00
}
field.Initialize(0);
RegisterValueChanged(field,
(valueString) =>
{
return Enum.Parse(property.PropertyType, valueString);
});
field.ValueChanged += (s, e) =>
{
2018-06-09 07:38:27 -07:00
property.SetValue(Enum.Parse(property.PropertyType, field.Value));
object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties));
propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
};
rowContainer = CreateSettingsRow(property, field, theme);
}
else if (propertyValue is IObject3D item
2019-02-04 08:41:08 -08:00
&& ApplicationController.Instance.Extensions.GetEditorsForType(property.PropertyType)?.FirstOrDefault() is IObject3DEditor iObject3DEditor)
{
2019-04-22 09:23:56 -07:00
// Use known IObject3D editors
rowContainer = iObject3DEditor.Create(item, undoBuffer, theme);
}
// remember the row name and widget
context.editRows.Add(property.PropertyInfo.Name, rowContainer);
return rowContainer;
}
private static GuiWidget CreateSourceChildSelector(SelectedChildren childSelector, OperationSourceContainerObject3D sourceContainer, ThemeConfig theme)
2019-02-11 18:16:05 -08:00
{
GuiWidget tabContainer = new FlowLayoutWidget(FlowDirection.TopToBottom);
var parentOfSubtractTargets = sourceContainer.SourceContainer.DescendantsAndSelfMultipleChildrenFirstOrSelf();
var sourceChildren = parentOfSubtractTargets.Children.ToList();
2019-02-11 18:16:05 -08:00
var objectChecks = new Dictionary<ICheckbox, IObject3D>();
var radioSiblings = new List<GuiWidget>();
for (int i = 0; i < sourceChildren.Count; i++)
{
var itemIndex = i;
var child = sourceChildren[itemIndex];
var rowContainer = new FlowLayoutWidget();
GuiWidget selectWidget;
if (sourceChildren.Count == 2)
{
var radioButton = new RadioButton(string.IsNullOrWhiteSpace(child.Name) ? $"{itemIndex}" : $"{child.Name}")
{
Checked = childSelector.Contains(child.ID),
2019-02-11 18:16:05 -08:00
TextColor = theme.TextColor
};
radioSiblings.Add(radioButton);
radioButton.SiblingRadioButtonList = radioSiblings;
selectWidget = radioButton;
}
else
{
selectWidget = new CheckBox(string.IsNullOrWhiteSpace(child.Name) ? $"{itemIndex}" : $"{child.Name}")
{
Checked = childSelector.Contains(child.ID),
2019-02-11 18:16:05 -08:00
TextColor = theme.TextColor
};
}
objectChecks.Add((ICheckbox)selectWidget, child);
rowContainer.AddChild(selectWidget);
var checkBox = selectWidget as ICheckbox;
checkBox.CheckedStateChanged += (s, e) =>
{
if (s is ICheckbox checkbox)
{
if (checkBox.Checked)
{
if (!childSelector.Contains(objectChecks[checkbox].ID))
2019-02-11 18:16:05 -08:00
{
childSelector.Add(objectChecks[checkbox].ID);
2019-02-11 18:16:05 -08:00
}
}
else
{
if (childSelector.Contains(objectChecks[checkbox].ID))
2019-02-11 18:16:05 -08:00
{
childSelector.Remove(objectChecks[checkbox].ID);
2019-02-11 18:16:05 -08:00
}
}
}
};
tabContainer.AddChild(rowContainer);
}
return tabContainer;
}
private static GuiWidget CreateSelector(SelectedChildren childSelector, IObject3D parent, ThemeConfig theme)
{
GuiWidget tabContainer = new FlowLayoutWidget(FlowDirection.TopToBottom);
void UpdateSelectColors(bool selectionChanged = false)
{
foreach (var child in parent.Children.ToList())
{
2018-06-20 08:09:35 -07:00
using (child.RebuildLock())
{
2018-06-20 08:09:35 -07:00
if (selectionChanged)
{
child.Visible = true;
}
}
}
}
tabContainer.Closed += (s, e) => UpdateSelectColors();
var children = parent.Children.ToList();
2019-04-22 09:23:56 -07:00
var objectChecks = new Dictionary<ICheckbox, IObject3D>();
2019-04-22 09:23:56 -07:00
var radioSiblings = new List<GuiWidget>();
for (int i = 0; i < children.Count; i++)
{
var itemIndex = i;
var child = children[itemIndex];
2019-04-22 09:23:56 -07:00
var rowContainer = new FlowLayoutWidget();
GuiWidget selectWidget;
if (children.Count == 2)
{
var radioButton = new RadioButton(string.IsNullOrWhiteSpace(child.Name) ? $"{itemIndex}" : $"{child.Name}")
{
Checked = childSelector.Contains(child.ID),
TextColor = theme.TextColor
};
radioSiblings.Add(radioButton);
radioButton.SiblingRadioButtonList = radioSiblings;
selectWidget = radioButton;
}
else
{
selectWidget = new CheckBox(string.IsNullOrWhiteSpace(child.Name) ? $"{itemIndex}" : $"{child.Name}")
{
Checked = childSelector.Contains(child.ID),
TextColor = theme.TextColor
};
}
objectChecks.Add((ICheckbox)selectWidget, child);
rowContainer.AddChild(selectWidget);
2019-04-22 09:23:56 -07:00
var checkBox = selectWidget as ICheckbox;
checkBox.CheckedStateChanged += (s, e) =>
{
if (s is ICheckbox checkbox)
{
if (checkBox.Checked)
{
if (!childSelector.Contains(objectChecks[checkbox].ID))
{
childSelector.Add(objectChecks[checkbox].ID);
}
}
else
{
if (childSelector.Contains(objectChecks[checkbox].ID))
{
childSelector.Remove(objectChecks[checkbox].ID);
}
}
2019-04-22 09:23:56 -07:00
if (parent is MeshWrapperObject3D meshWrapper)
{
2018-06-20 08:09:35 -07:00
using (meshWrapper.RebuildLock())
{
meshWrapper.ResetMeshWrapperMeshes(Object3DPropertyFlags.All, CancellationToken.None);
}
}
UpdateSelectColors(true);
}
};
tabContainer.AddChild(rowContainer);
UpdateSelectColors();
}
return tabContainer;
}
private void AddUnlockLinkIfRequired(PPEContext context, FlowLayoutWidget editControlsContainer, ThemeConfig theme)
{
2019-05-20 21:23:45 -07:00
var unlockUrl = ApplicationController.Instance.GetUnlockPage?.Invoke(context.item);
if (!context.item.Persistable
&& !string.IsNullOrEmpty(unlockUrl))
{
FlowLayoutWidget row = GetUnlockRow(theme, unlockUrl);
editControlsContainer.AddChild(row);
}
}
2018-12-31 12:12:05 -08:00
public static FlowLayoutWidget GetUnlockRow(ThemeConfig theme, string unlockLinkUrl)
{
var row = CreateSettingsRow("Demo Mode".Localize());
var detailsLink = new TextIconButton("Unlock".Localize(), AggContext.StaticData.LoadIcon("locked.png", 16, 16, theme.InvertIcons), theme)
{
Margin = 5,
ToolTipText = "Visit MatterHackers.com to Purchase".Localize()
2018-12-31 12:12:05 -08:00
};
detailsLink.Click += (s, e) =>
{
ApplicationController.Instance.LaunchBrowser(unlockLinkUrl);
2018-12-31 12:12:05 -08:00
};
row.AddChild(detailsLink);
theme.ApplyPrimaryActionStyle(detailsLink);
return row;
}
private void AddWebPageLinkIfRequired(PPEContext context, FlowLayoutWidget editControlsContainer, ThemeConfig theme)
{
2019-04-22 09:23:56 -07:00
if (context.item.GetType().GetCustomAttributes(typeof(WebPageLinkAttribute), true).FirstOrDefault() is WebPageLinkAttribute unlockLink)
{
var row = CreateSettingsRow("Website".Localize());
var detailsLink = new TextIconButton(unlockLink.Name.Localize(), AggContext.StaticData.LoadIcon("internet.png", 16, 16, theme.InvertIcons), theme)
{
BackgroundColor = theme.MinimalShade,
ToolTipText = unlockLink.Url,
};
detailsLink.Click += (s, e) =>
{
ApplicationController.Instance.LaunchBrowser(unlockLink.Url);
};
row.AddChild(detailsLink);
editControlsContainer.AddChild(row);
}
}
}
}