public static GuiWidget CreatePropertyEditor(EditableProperty property, UndoBuffer undoBuffer, PPEContext context, ThemeConfig theme) { var object3D = property.Item; 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) => { var newValue = field.Value; var oldValue = property.Value.ToString(); if (valueToString != null) { oldValue = valueToString(property.Value); } // field.Content if (undoBuffer != null) { 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 { property.SetValue(valueFromString(newValue)); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); } }; } var readOnly = property.PropertyInfo.GetCustomAttributes(true).OfType <ReadOnlyAttribute>().FirstOrDefault() != null; // create a double editor if (propertyValue is double doubleValue) { if (readOnly) { var valueField = new TextWidget(string.Format("{0:n}", doubleValue), textColor: theme.TextColor, pointSize: 10) { AutoExpandBoundsToText = true }; rowContainer = new SettingsRow(property.DisplayName.Localize(), property.Description, valueField, theme); void RefreshField(object s, InvalidateArgs e) { if (e.InvalidateType.HasFlag(InvalidateType.DisplayValues)) { double newValue = (double)property.Value; valueField.Text = string.Format("{0:n}", newValue); } } object3D.Invalidated += RefreshField; valueField.Closed += (s, e) => object3D.Invalidated -= RefreshField; } else // normal edit row { var field = new DoubleField(theme); field.Initialize(0); field.DoubleValue = doubleValue; field.ClearUndoHistory(); 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.Descendants <InternalTextEditWidget>().First().Name = property.DisplayName + " Edit"; field.Content.Closed += (s, e) => object3D.Invalidated -= RefreshField; rowContainer = CreateSettingsRow(property, field, theme); } } 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, theme); } else if (propertyValue is Vector2 vector2) { var field = new Vector2Field(theme); field.Initialize(0); field.Vector2 = vector2; field.ClearUndoHistory(); RegisterValueChanged(field, (valueString) => Vector2.Parse(valueString), (value) => { var s = ((Vector2)value).ToString(); return(s.Substring(1, s.Length - 2)); }); rowContainer = CreateSettingsColumn(property, field); } else if (propertyValue is Vector3 vector3) { var field = new Vector3Field(theme); field.Initialize(0); field.Vector3 = vector3; field.ClearUndoHistory(); RegisterValueChanged( field, (valueString) => Vector3.Parse(valueString), (value) => { var s = ((Vector3)value).ToString(); return(s.Substring(1, s.Length - 2)); }); rowContainer = CreateSettingsColumn(property, field); } else if (propertyValue is DirectionVector directionVector) { var field = new DirectionVectorField(theme); field.Initialize(0); field.SetValue(directionVector); field.ClearUndoHistory(); field.ValueChanged += (s, e) => { property.SetValue(field.DirectionVector); 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 DirectionAxis directionAxis) { rowContainer = CreateSettingsColumn(property); var field1 = new DirectionVectorField(theme); field1.Initialize(0); field1.ClearUndoHistory(); field1.SetValue(new DirectionVector() { Normal = directionAxis.Normal }); rowContainer.AddChild(new SettingsRow("Axis".Localize(), null, field1.Content, theme)); // the direction axis // the distance from the center of the part // create a double editor var field2 = new Vector3Field(theme); field2.Initialize(0); field2.Vector3 = directionAxis.Origin - property.Item.Children.First().GetAxisAlignedBoundingBox().Center; field2.ClearUndoHistory(); var row2 = CreateSettingsColumn("Offset".Localize(), field2); // update this when changed void UpdateData(object s, InvalidateArgs e) { field2.Vector3 = ((DirectionAxis)property.Value).Origin - property.Item.Children.First().GetAxisAlignedBoundingBox().Center; } property.Item.Invalidated += UpdateData; field2.Content.Closed += (s, e) => { property.Item.Invalidated -= UpdateData; }; // update functions field1.ValueChanged += (s, e) => { property.SetValue(new DirectionAxis() { Normal = field1.DirectionVector.Normal, 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, 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); } return(childrenSelector); }); rowContainer = CreateSettingsRow(property, field, theme); } else // show the subtract editor for boolean subtract and subtract and replace { rowContainer = CreateSettingsColumn(property); if (property.Item is OperationSourceContainerObject3D sourceContainer) { Action selected = null; if (!(context.item.GetType().GetCustomAttributes(typeof(ShowUpdateButtonAttribute), true).FirstOrDefault() is ShowUpdateButtonAttribute showUpdate)) { selected = () => { object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties)); }; } rowContainer.AddChild(CreateSourceChildSelector(childSelector, sourceContainer, theme, selected)); } else { rowContainer.AddChild(CreateSelector(childSelector, property.Item, theme)); } } } else if (propertyValue is ImageBuffer imageBuffer) { rowContainer = CreateSettingsColumn(property); rowContainer.AddChild(new ImageWidget(imageBuffer) { HAnchor = HAnchor.Left, Margin = new BorderDouble(0, 3) }); } #if !__ANDROID__ else if (propertyValue is List <string> stringList) { 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) { if (readOnly) { var valueField = new TextWidget(string.Format("{0:n0}", intValue), textColor: theme.TextColor, pointSize: 10) { AutoExpandBoundsToText = true }; rowContainer = new SettingsRow(property.DisplayName.Localize(), property.Description, valueField, theme); void RefreshField(object s, InvalidateArgs e) { if (e.InvalidateType.HasFlag(InvalidateType.DisplayValues)) { int newValue = (int)property.Value; valueField.Text = string.Format("{0:n0}", newValue); } } object3D.Invalidated += RefreshField; valueField.Closed += (s, e) => object3D.Invalidated -= RefreshField; } else // normal edit row { var field = new IntField(theme); field.Initialize(0); field.IntValue = intValue; field.ClearUndoHistory(); 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, theme); } } else if (propertyValue is bool boolValue) { // create a bool editor var field = new ToggleboxField(theme); field.Initialize(0); field.Checked = boolValue; RegisterValueChanged(field, (valueString) => { return(valueString == "1"); }, (value) => { return(((bool)value) ? "1" : "0"); }); rowContainer = CreateSettingsRow(property, field, theme); } else if (propertyValue is string stringValue) { if (readOnly) { rowContainer = new WrappedTextWidget(stringValue, textColor: theme.TextColor, pointSize: 10) { Margin = 5 }; } else // normal edit row { var multiLineEditAttribute = property.PropertyInfo.GetCustomAttributes(true).OfType <MultiLineEditAttribute>().FirstOrDefault(); if (multiLineEditAttribute != null) { // create a a multi-line string editor var field = new MultilineStringField(theme); field.Initialize(0); field.SetValue(stringValue, false); field.ClearUndoHistory(); field.Content.HAnchor = HAnchor.Stretch; // field.Content.MinimumSize = new Vector2(0, 200 * GuiWidget.DeviceScale); RegisterValueChanged(field, (valueString) => valueString); rowContainer = CreateSettingsColumn(property, field, fullWidth: true); } else { // create a string editor var field = new TextField(theme); field.Initialize(0); field.SetValue(stringValue, false); field.ClearUndoHistory(); field.Content.HAnchor = HAnchor.Stretch; RegisterValueChanged(field, (valueString) => valueString); rowContainer = CreateSettingsRow(property, field, theme); var label = rowContainer.Children.First(); 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) { // create a char editor var field = new CharField(theme); field.Initialize(0); field.SetValue(charValue.ToString(), false); field.ClearUndoHistory(); field.ValueChanged += (s, e) => { property.SetValue(Convert.ToChar(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 (property.PropertyType.IsEnum) { // create an enum editor UIField field; var enumDisplayAttribute = property.PropertyInfo.GetCustomAttributes(true).OfType <EnumDisplayAttribute>().FirstOrDefault(); var addToSettingsRow = true; if (enumDisplayAttribute != null) { field = new EnumDisplayField(property, enumDisplayAttribute, theme) { InitialValue = propertyValue.ToString(), }; if (enumDisplayAttribute.Mode == EnumDisplayAttribute.PresentationMode.Tabs) { addToSettingsRow = false; } } else { if (property.PropertyType == typeof(NamedTypeFace)) { field = new FontSelectorField(property, theme); } else { field = new EnumField(property, theme); } } field.Initialize(0); RegisterValueChanged(field, (valueString) => { return(Enum.Parse(property.PropertyType, valueString)); }); field.ValueChanged += (s, e) => { if (property.Value.ToString() != field.Value) { property.SetValue(Enum.Parse(property.PropertyType, field.Value)); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); } }; if (addToSettingsRow) { rowContainer = CreateSettingsRow(property, field, theme); } else { // field.Content.Margin = new BorderDouble(3, 0); field.Content.HAnchor = HAnchor.Stretch; rowContainer = field.Content; } } else if (propertyValue is IObject3D item && ApplicationController.Instance.Extensions.GetEditorsForType(property.PropertyType)?.FirstOrDefault() is IObject3DEditor iObject3DEditor) { // 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); }
public void SetActiveItem(IObject3D selectedItem) { this.item = selectedItem; editorPanel.CloseAllChildren(); // Allow caller to clean up with passing null for selectedItem if (item == null) { return; } var selectedItemType = selectedItem.GetType(); editorSectionWidget.Text = selectedItem.Name ?? selectedItemType.Name; HashSet <IObject3DEditor> mappedEditors = ApplicationController.Instance.GetEditorsForType(selectedItemType); var undoBuffer = sceneContext.Scene.UndoBuffer; bool allowOperations = true; // put in a color edit field var colorField = new ColorField(theme, selectedItem.Color); colorField.Initialize(0); colorField.ValueChanged += (s, e) => { if (selectedItem.Color != colorField.Color) { undoBuffer.AddAndDo(new ChangeColor(selectedItem, colorField.Color)); } }; var colorRow = PublicPropertyEditor.CreateSettingsRow("Color".Localize()); colorRow.AddChild(colorField.Content); colorField.Content.MouseDown += (s, e) => { // make sure the render mode is set to shaded or outline if (sceneContext.ViewState.RenderType != RenderOpenGl.RenderTypes.Shaded && sceneContext.ViewState.RenderType != RenderOpenGl.RenderTypes.Outlines) { // make sure the render mode is set to material sceneContext.ViewState.RenderType = RenderOpenGl.RenderTypes.Outlines; } }; editorPanel.AddChild(colorRow); // put in a material edit field var materialField = new MaterialIndexField(theme, selectedItem.MaterialIndex); materialField.Initialize(0); materialField.ValueChanged += (s, e) => { if (selectedItem.MaterialIndex != materialField.MaterialIndex) { undoBuffer.AddAndDo(new ChangeMaterial(selectedItem, materialField.MaterialIndex)); } }; var materialRow = PublicPropertyEditor.CreateSettingsRow("Material".Localize()); materialRow.AddChild(materialField.Content); materialField.Content.MouseDown += (s, e) => { if (sceneContext.ViewState.RenderType != RenderOpenGl.RenderTypes.Materials) { // make sure the render mode is set to material sceneContext.ViewState.RenderType = RenderOpenGl.RenderTypes.Materials; } }; editorPanel.AddChild(materialRow); // put in the normal editor if (selectedItem is ComponentObject3D componentObject && componentObject.Finalized) { allowOperations = false; foreach (var selector in componentObject.SurfacedEditors) { // Get the named property via reflection // Selector example: '$.Children<CylinderObject3D>' var match = pathResolver.Select(componentObject, selector).ToList(); //// TODO: Create editor row for each property //// - Use the type of the property to find a matching editor (ideally all datatype -> editor functionality would resolve consistently) //// - Add editor row for each foreach (var instance in match) { if (instance is IObject3D object3D) { if (ApplicationController.Instance.GetEditorsForType(object3D.GetType())?.FirstOrDefault() is IObject3DEditor editor) { ShowObjectEditor((editor, object3D, object3D.Name), selectedItem, allowOperations: allowOperations); } } else if (JsonPath.JsonPathContext.ReflectionValueSystem.LastMemberValue is ReflectionTarget reflectionTarget) { var context = new PPEContext(); if (reflectionTarget.Source is IObject3D editedChild) { context.item = editedChild; } else { context.item = item; } var editableProperty = new EditableProperty(reflectionTarget.PropertyInfo, reflectionTarget.Source); var editor = PublicPropertyEditor.CreatePropertyEditor(editableProperty, undoBuffer, context, theme); if (editor != null) { editorPanel.AddChild(editor); } } } } // Enforce panel padding foreach (var sectionWidget in editorPanel.Descendants <SectionWidget>()) { sectionWidget.Margin = new BorderDouble(0, theme.DefaultContainerPadding / 2); } }
public void SetActiveItem(ISceneContext sceneContext) { var selectedItem = sceneContext?.Scene?.SelectedItem; if (this.item == selectedItem) { return; } this.item = selectedItem; editorPanel.CloseChildren(); // Allow caller to clean up with passing null for selectedItem if (item == null) { editorSectionWidget.Text = editorTitle; return; } var selectedItemType = selectedItem.GetType(); primaryActionsPanel.RemoveChildren(); IEnumerable <SceneOperation> primaryActions; if ((primaryActions = SceneOperations.GetPrimaryOperations(selectedItemType)) == null) { primaryActions = new List <SceneOperation>(); } else { // Loop over primary actions creating a button for each foreach (var primaryAction in primaryActions) { // TODO: Run visible/enable rules on actions, conditionally add/enable as appropriate var button = new IconButton(primaryAction.Icon(theme.InvertIcons), theme) { // Name = namedAction.Title + " Button", ToolTipText = primaryAction.Title, Margin = theme.ButtonSpacing, BackgroundColor = theme.ToolbarButtonBackground, HoverColor = theme.ToolbarButtonHover, MouseDownColor = theme.ToolbarButtonDown, }; button.Click += (s, e) => { primaryAction.Action.Invoke(sceneContext); }; primaryActionsPanel.AddChild(button); } } if (primaryActionsPanel.Children.Any()) { // add in a separator from the apply and cancel buttons primaryActionsPanel.AddChild(new ToolbarSeparator(theme.GetBorderColor(50), theme.SeparatorMargin)); } editorSectionWidget.Text = selectedItem.Name ?? selectedItemType.Name; HashSet <IObject3DEditor> mappedEditors = ApplicationController.Instance.Extensions.GetEditorsForType(selectedItemType); var undoBuffer = sceneContext.Scene.UndoBuffer; // put in a color edit field var colorField = new ColorField(theme, selectedItem.Color); colorField.Initialize(0); colorField.ValueChanged += (s, e) => { if (selectedItem.Color != colorField.Color) { undoBuffer.AddAndDo(new ChangeColor(selectedItem, colorField.Color)); } }; colorField.Content.MouseDown += (s, e) => { // make sure the render mode is set to shaded or outline if (sceneContext.ViewState.RenderType != RenderOpenGl.RenderTypes.Shaded && sceneContext.ViewState.RenderType != RenderOpenGl.RenderTypes.Outlines) { // make sure the render mode is set to outline sceneContext.ViewState.RenderType = RenderOpenGl.RenderTypes.Outlines; } }; // color row var row = new SettingsRow("Color".Localize(), null, colorField.Content, theme); // Special top border style for first item in editor row.Border = new BorderDouble(0, 1); editorPanel.AddChild(row); // put in a material edit field var materialField = new MaterialIndexField(sceneContext.Printer, theme, selectedItem.MaterialIndex); materialField.Initialize(0); materialField.ValueChanged += (s, e) => { if (selectedItem.MaterialIndex != materialField.MaterialIndex) { undoBuffer.AddAndDo(new ChangeMaterial(selectedItem, materialField.MaterialIndex)); } }; materialField.Content.MouseDown += (s, e) => { if (sceneContext.ViewState.RenderType != RenderOpenGl.RenderTypes.Materials) { // make sure the render mode is set to material sceneContext.ViewState.RenderType = RenderOpenGl.RenderTypes.Materials; } }; // material row editorPanel.AddChild( new SettingsRow("Material".Localize(), null, materialField.Content, theme)); // put in the normal editor if (selectedItem is ComponentObject3D componentObject && componentObject.Finalized) { PublicPropertyEditor.AddUnlockLinkIfRequired(selectedItem, editorPanel, theme); foreach (var selector in componentObject.SurfacedEditors) { // Get the named property via reflection // Selector example: '$.Children<CylinderObject3D>' var match = pathResolver.Select(componentObject, selector).ToList(); //// TODO: Create editor row for each property //// - Use the type of the property to find a matching editor (ideally all datatype -> editor functionality would resolve consistently) //// - Add editor row for each foreach (var instance in match) { if (instance is IObject3D object3D) { if (ApplicationController.Instance.Extensions.GetEditorsForType(object3D.GetType())?.FirstOrDefault() is IObject3DEditor editor) { ShowObjectEditor((editor, object3D, object3D.Name), selectedItem); } } else if (JsonPath.JsonPathContext.ReflectionValueSystem.LastMemberValue is ReflectionTarget reflectionTarget) { var context = new PPEContext(); if (reflectionTarget.Source is IObject3D editedChild) { context.item = editedChild; } else { context.item = item; } var editableProperty = new EditableProperty(reflectionTarget.PropertyInfo, reflectionTarget.Source); var editor = PublicPropertyEditor.CreatePropertyEditor(editableProperty, undoBuffer, context, theme); if (editor != null) { editorPanel.AddChild(editor); } } } } // Enforce panel padding foreach (var sectionWidget in editorPanel.Descendants <SectionWidget>()) { sectionWidget.Margin = 0; } }
public static GuiWidget CreatePropertyEditor(EditableProperty property, UndoBuffer undoBuffer, PPEContext context, ThemeConfig theme) { var object3D = property.Item; var propertyGridModifier = property.Item as IPropertyGridModifier; GuiWidget rowContainer = null; // Get reflected property value once, then test for each case below var propertyValue = property.Value; // create a double editor if (propertyValue is double doubleValue) { var field = new DoubleField(); field.Initialize(0); field.DoubleValue = doubleValue; field.ValueChanged += (s, e) => { undoBuffer.AddAndDo(new UndoRedoDoubleField(field.DoubleValue, property, object3D, context, undoBuffer)); }; void RefreshField(object s, InvalidateArgs e) { if (e.InvalidateType == InvalidateType.Properties) { 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, undoBuffer)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); }; rowContainer = CreateSettingsRow(property, field); } else if (propertyValue is Vector2 vector2) { var field = new Vector2Field(); field.Initialize(0); field.Vector2 = vector2; field.ValueChanged += (s, e) => { property.SetValue(field.Vector2); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties, undoBuffer)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); }; rowContainer = CreateSettingsColumn(property, field); } else if (propertyValue is Vector3 vector3) { var field = new Vector3Field(); field.Initialize(0); field.Vector3 = vector3; field.ValueChanged += (s, e) => { property.SetValue(field.Vector3); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties, undoBuffer)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); }; rowContainer = CreateSettingsColumn(property, field); } else if (propertyValue is DirectionVector directionVector) { var field = new DirectionVectorField(theme); field.Initialize(0); field.SetValue(directionVector); field.ValueChanged += (s, e) => { property.SetValue(field.DirectionVector); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties, undoBuffer)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); }; 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()); var field1 = new DirectionVectorField(theme); field1.Initialize(0); field1.SetValue(newDirectionVector); row1.AddChild(field1.Content); rowContainer.AddChild(row1); // the direction axis // the distance from the center of the part // create a double editor var field2 = new Vector3Field(); field2.Initialize(0); field2.Vector3 = directionAxis.Origin - property.Item.Children.First().GetAxisAlignedBoundingBox().Center; var row2 = CreateSettingsColumn("Offset", field2); // update this when changed EventHandler <InvalidateArgs> updateData = (s, e) => { field2.Vector3 = ((DirectionAxis)property.Value).Origin - property.Item.Children.First().GetAxisAlignedBoundingBox().Center; }; property.Item.Invalidated += updateData; field2.Content.Closed += (s, e) => { property.Item.Invalidated -= updateData; }; // update functions field1.ValueChanged += (s, e) => { property.SetValue(new DirectionAxis() { Normal = field1.DirectionVector.Normal, Origin = property.Item.Children.First().GetAxisAlignedBoundingBox().Center + field2.Vector3 }); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties, undoBuffer)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); }; field2.ValueChanged += (s, e) => { property.SetValue(new DirectionAxis() { Normal = field1.DirectionVector.Normal, Origin = property.Item.Children.First().GetAxisAlignedBoundingBox().Center + field2.Vector3 }); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties, undoBuffer)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); }; rowContainer.AddChild(row2); } else if (propertyValue is ChildrenSelector childSelector) { var showAsList = property.PropertyInfo.GetCustomAttributes(true).OfType <ShowAsListAttribute>().FirstOrDefault() != null; if (showAsList) { UIField field = new ChildrenSelectorListField(property, theme); field.Initialize(0); field.ValueChanged += (s, e) => { property.SetValue(new ChildrenSelector() { field.Value }); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties, undoBuffer)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); }; rowContainer = CreateSettingsRow(property, field); } else // show the subtarct editor for boolean subtract and subtract and replace { rowContainer = CreateSettingsColumn(property); rowContainer.AddChild(CreateSelector(childSelector, property.Item, theme)); } } else if (propertyValue is ImageBuffer imageBuffer) { rowContainer = CreateSettingsColumn(property); rowContainer.AddChild(CreateImageDisplay(imageBuffer, property.Item, theme)); } #if !__ANDROID__ else if (propertyValue is List <string> stringList) { var selectedItem = ApplicationController.Instance.DragDropData.SceneContext.Scene.SelectedItem; var field = new SurfacedEditorsField(theme, selectedItem); 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(); field.Initialize(0); field.IntValue = intValue; field.ValueChanged += (s, e) => { undoBuffer.AddAndDo(new UndoRedoIntField(field.IntValue, property, object3D, context, undoBuffer)); }; void RefreshField(object s, InvalidateArgs e) { if (e.InvalidateType == InvalidateType.Properties) { 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); } // create a bool editor else if (propertyValue is bool boolValue) { var field = new ToggleboxField(theme); field.Initialize(0); field.Checked = boolValue; field.ValueChanged += (s, e) => { property.SetValue(field.Checked); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties, undoBuffer)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); }; rowContainer = CreateSettingsRow(property, field); } // create a string editor else if (propertyValue is string stringValue) { var field = new TextField(); field.Initialize(0); field.SetValue(stringValue, false); field.Content.HAnchor = HAnchor.Stretch; field.ValueChanged += (s, e) => { property.SetValue(field.Value); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties, undoBuffer)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); }; 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); } } // create a char editor else if (propertyValue is char charValue) { var field = new CharField(); field.Initialize(0); field.SetValue(charValue.ToString(), false); field.ValueChanged += (s, e) => { property.SetValue(Convert.ToChar(field.Value)); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties, undoBuffer)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); }; rowContainer = CreateSettingsRow(property, field); } // create an enum editor else if (property.PropertyType.IsEnum) { UIField field; var iconsAttribute = property.PropertyInfo.GetCustomAttributes(true).OfType <IconsAttribute>().FirstOrDefault(); if (iconsAttribute != null) { field = new IconEnumField(property, iconsAttribute, theme) { InitialValue = propertyValue.ToString() }; } else { field = new EnumField(property, theme); } field.Initialize(0); field.ValueChanged += (s, e) => { property.SetValue(Enum.Parse(property.PropertyType, field.Value)); object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties, undoBuffer)); propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name)); }; rowContainer = CreateSettingsRow(property, field); } // Use known IObject3D editors else if (propertyValue is IObject3D item && ApplicationController.Instance.GetEditorsForType(property.PropertyType)?.FirstOrDefault() is IObject3DEditor iObject3DEditor) { rowContainer = iObject3DEditor.Create(item, theme); } // remember the row name and widget context.editRows.Add(property.PropertyInfo.Name, rowContainer); return(rowContainer); }
public void SetActiveItem(ISceneContext sceneContext) { var selectedItem = sceneContext?.Scene?.SelectedItem; if (this.item == selectedItem) { return; } this.item = selectedItem; editorPanel.CloseChildren(); // Allow caller to clean up with passing null for selectedItem if (item == null) { editorSectionWidget.Text = editorTitle; return; } var selectedItemType = selectedItem.GetType(); primaryActionsPanel.RemoveChildren(); IEnumerable <SceneOperation> primaryActions; if ((primaryActions = SceneOperations.GetPrimaryOperations(selectedItemType)) == null) { primaryActions = new List <SceneOperation>(); } else { // Loop over primary actions creating a button for each foreach (var primaryAction in primaryActions) { // TODO: Run visible/enable rules on actions, conditionally add/enable as appropriate var button = new IconButton(primaryAction.Icon(theme), theme) { // Name = namedAction.Title + " Button", ToolTipText = primaryAction.Title, Margin = theme.ButtonSpacing, BackgroundColor = theme.ToolbarButtonBackground, HoverColor = theme.ToolbarButtonHover, MouseDownColor = theme.ToolbarButtonDown, }; button.Click += (s, e) => { primaryAction.Action.Invoke(sceneContext); }; primaryActionsPanel.AddChild(button); } } if (primaryActionsPanel.Children.Any()) { // add in a separator from the apply and cancel buttons primaryActionsPanel.AddChild(new ToolbarSeparator(theme.GetBorderColor(50), theme.SeparatorMargin)); } editorSectionWidget.Text = selectedItem.Name ?? selectedItemType.Name; HashSet <IObject3DEditor> mappedEditors = ApplicationController.Instance.Extensions.GetEditorsForType(selectedItemType); var undoBuffer = sceneContext.Scene.UndoBuffer; if (!(selectedItem.GetType().GetCustomAttributes(typeof(HideMeterialAndColor), true).FirstOrDefault() is HideMeterialAndColor)) { // put in a color edit field var colorField = new ColorField(theme, selectedItem.Color); colorField.Initialize(0); colorField.ValueChanged += (s, e) => { if (selectedItem.Color != colorField.Color) { undoBuffer.AddAndDo(new ChangeColor(selectedItem, colorField.Color)); } }; colorField.Content.MouseDown += (s, e) => { // make sure the render mode is set to shaded or outline if (sceneContext.ViewState.RenderType != RenderOpenGl.RenderTypes.Shaded && sceneContext.ViewState.RenderType != RenderOpenGl.RenderTypes.Outlines) { // make sure the render mode is set to outline sceneContext.ViewState.RenderType = RenderOpenGl.RenderTypes.Outlines; } }; // color row var row = new SettingsRow("Color".Localize(), null, colorField.Content, theme); // Special top border style for first item in editor row.Border = new BorderDouble(0, 1); editorPanel.AddChild(row); // put in a material edit field var materialField = new MaterialIndexField(sceneContext.Printer, theme, selectedItem.MaterialIndex); materialField.Initialize(0); materialField.ValueChanged += (s, e) => { if (selectedItem.MaterialIndex != materialField.MaterialIndex) { undoBuffer.AddAndDo(new ChangeMaterial(selectedItem, materialField.MaterialIndex)); } }; materialField.Content.MouseDown += (s, e) => { if (sceneContext.ViewState.RenderType != RenderOpenGl.RenderTypes.Materials) { // make sure the render mode is set to material sceneContext.ViewState.RenderType = RenderOpenGl.RenderTypes.Materials; } }; // material row editorPanel.AddChild( new SettingsRow("Material".Localize(), null, materialField.Content, theme)); } var rows = new SafeList <SettingsRow>(); // put in the normal editor if (selectedItem is ComponentObject3D componentObject && componentObject.Finalized) { var context = new PPEContext(); PublicPropertyEditor.AddUnlockLinkIfRequired(selectedItem, editorPanel, theme); foreach (var selector in componentObject.SurfacedEditors) { // if it is a reference to a sheet cell if (selector.StartsWith("!")) { var firtSheet = componentObject.Descendants <SheetObject3D>().FirstOrDefault(); if (firtSheet != null) { var cellId = selector.Substring(1); var cell = firtSheet.SheetData[cellId]; if (cell != null) { // add an editor for the cell var field = new DoubleField(theme); field.Initialize(0); double.TryParse(firtSheet.SheetData.EvaluateExpression(cellId), out double value); field.DoubleValue = value; field.ClearUndoHistory(); field.Content.Descendants <InternalNumberEdit>().First().MaxDecimalsPlaces = 3; field.ValueChanged += (s, e) => { cell.Expression = field.Value; firtSheet.SheetData.Recalculate(); }; var row = new SettingsRow(cell.Name == null ? cellId : cell.Name, null, field.Content, theme); editorPanel.AddChild(row); } } } else // parse it as a path to an object { // Get the named property via reflection // Selector example: '$.Children<CylinderObject3D>' var match = pathResolver.Select(componentObject, selector).ToList(); //// - Add editor row for each foreach (var instance in match) { if (instance is IObject3D object3D) { if (ApplicationController.Instance.Extensions.GetEditorsForType(object3D.GetType())?.FirstOrDefault() is IObject3DEditor editor) { ShowObjectEditor((editor, object3D, object3D.Name), selectedItem); } } else if (JsonPathContext.ReflectionValueSystem.LastMemberValue is ReflectionTarget reflectionTarget) { if (reflectionTarget.Source is IObject3D editedChild) { context.item = editedChild; } else { context.item = item; } var editableProperty = new EditableProperty(reflectionTarget.PropertyInfo, reflectionTarget.Source); var editor = PublicPropertyEditor.CreatePropertyEditor(rows, editableProperty, undoBuffer, context, theme); if (editor != null) { editorPanel.AddChild(editor); } // Init with custom 'UpdateControls' hooks (context.item as IPropertyGridModifier)?.UpdateControls(new PublicPropertyChange(context, "Update_Button")); } } } } // Enforce panel padding foreach (var sectionWidget in editorPanel.Descendants <SectionWidget>()) { sectionWidget.Margin = 0; } }