Ejemplo n.º 1
0
        private static void SetValue(EditableProperty property, PPEContext context, Func <string, object> valueFromString, double sliderDownValue)
        {
            var localItem            = context.item;
            var object3D             = property.Item;
            var propertyGridModifier = property.Item as IPropertyGridModifier;

            property.SetValue(valueFromString(sliderDownValue.ToString()));
            object3D?.Invalidate(new InvalidateArgs(localItem, InvalidateType.Properties));
            propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));

            object3D?.Invalidate(new InvalidateArgs(localItem, InvalidateType.DisplayValues));
        }
        private void AddUnlockLinkIfRequired(PPEContext context, FlowLayoutWidget editControlsContainer, ThemeConfig theme)
        {
            var unlockLink = context.item.GetType().GetCustomAttributes(typeof(UnlockLinkAttribute), true).FirstOrDefault() as UnlockLinkAttribute;

            if (unlockLink != null &&
                !string.IsNullOrEmpty(unlockLink.UnlockPageLink) &&
                !context.item.Persistable)
            {
                FlowLayoutWidget row = GetUnlockRow(theme, unlockLink.UnlockPageLink);

                editControlsContainer.AddChild(row);
            }
        }
Ejemplo n.º 3
0
        private void AddWebPageLinkIfRequired(PPEContext context, FlowLayoutWidget editControlsContainer, ThemeConfig theme)
        {
            if (context.item.GetType().GetCustomAttributes(typeof(WebPageLinkAttribute), true).FirstOrDefault() is WebPageLinkAttribute unlockLink)
            {
                var detailsLink = new TextIconButton(unlockLink.Name.Localize(), StaticData.Instance.LoadIcon("internet.png", 16, 16, theme.InvertIcons), theme)
                {
                    BackgroundColor = theme.MinimalShade,
                    ToolTipText     = unlockLink.Url,
                };
                detailsLink.Click += (s, e) =>
                {
                    ApplicationController.LaunchBrowser(unlockLink.Url);
                };

                // website row
                editControlsContainer.AddChild(new SettingsRow("Website".Localize(), null, detailsLink, theme));
            }
        }
        private void AddUnlockLinkIfRequired(PPEContext context, FlowLayoutWidget editControlsContainer, ThemeConfig theme)
        {
            var unlockLink = context.item.GetType().GetCustomAttributes(typeof(UnlockLinkAttribute), true).FirstOrDefault() as UnlockLinkAttribute;

            if (unlockLink != null &&
                !string.IsNullOrEmpty(unlockLink.UnlockPageLink) &&
                !context.item.Persistable)
            {
                var row = CreateSettingsRow(context.item.Persistable ? "Registered".Localize() : "Demo Mode".Localize());

                var detailsLink = new TextIconButton("Unlock".Localize(), AggContext.StaticData.LoadIcon("locked.png", 16, 16, theme.InvertIcons), theme)
                {
                    Margin = 5
                };
                detailsLink.Click += (s, e) =>
                {
                    ApplicationController.Instance.LaunchBrowser(UnlockLinkAttribute.UnlockPageBaseUrl + unlockLink.UnlockPageLink);
                };
                row.AddChild(detailsLink);
                theme.ApplyPrimaryActionStyle(detailsLink);

                editControlsContainer.AddChild(row);
            }
        }
        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) =>
                {
                    property.SetValue(field.DoubleValue);
                    object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties, undoBuffer));
                    propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
                };

                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 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) =>
                {
                    property.SetValue(field.IntValue);
                    object3D?.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties, undoBuffer));
                    propertyGridModifier?.UpdateControls(new PublicPropertyChange(context, property.PropertyInfo.Name));
                };

                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 GuiWidget Create(IObject3D item, ThemeConfig theme)
        {
            var mainContainer = new FlowLayoutWidget(FlowDirection.TopToBottom)
            {
                HAnchor = HAnchor.Stretch
            };

            // TODO: Long term we should have a solution where editors can extend Draw and Undo without this hack
            var view3DWidget = ApplicationController.Instance.DragDropData.View3DWidget;
            var undoBuffer   = view3DWidget.sceneContext.Scene.UndoBuffer;

            if (item is IEditorDraw editorDraw)
            {
                // TODO: Putting the drawing code in the IObject3D means almost certain bindings to MatterControl in IObject3D. If instead
                // we had a UI layer object that used binding to register scene drawing hooks for specific types, we could avoid the bindings
                view3DWidget.InteractionLayer.DrawGlOpaqueContent += editorDraw.DrawEditor;
                mainContainer.Closed += (s, e) =>
                {
                    view3DWidget.InteractionLayer.DrawGlOpaqueContent -= editorDraw.DrawEditor;
                };
            }

            if (item != null)
            {
                var context = new PPEContext()
                {
                    item = item
                };

                // CreateEditor
                AddUnlockLinkIfRequired(context, mainContainer, theme);

                // Create a field editor for each editable property detected via reflection
                foreach (var property in GetEditablePropreties(context.item))
                {
                    var editor = CreatePropertyEditor(property, undoBuffer, context, theme);
                    if (editor != null)
                    {
                        mainContainer.AddChild(editor);
                    }
                }

                AddWebPageLinkIfRequired(context, mainContainer, theme);

                // add in an Update button if applicable
                var showUpdate = context.item.GetType().GetCustomAttributes(typeof(ShowUpdateButtonAttribute), true).FirstOrDefault() as ShowUpdateButtonAttribute;
                if (showUpdate != null)
                {
                    var updateButton = new TextButton("Update".Localize(), theme)
                    {
                        Margin          = 5,
                        BackgroundColor = theme.MinimalShade,
                        HAnchor         = HAnchor.Right,
                        VAnchor         = VAnchor.Absolute
                    };
                    updateButton.Click += (s, e) =>
                    {
                        context.item.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties, undoBuffer));
                    };
                    mainContainer.AddChild(updateButton);
                }

                // Init with custom 'UpdateControls' hooks
                (context.item as IPropertyGridModifier)?.UpdateControls(new PublicPropertyChange(context, "Update_Button"));
            }

            return(mainContainer);
        }
 public PublicPropertyChange(PPEContext pPEContext, string propertyChanged)
 {
     this.Context = pPEContext;
     this.Changed = propertyChanged;
 }
Ejemplo n.º 8
0
        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
                AddUnlockLinkIfRequired(context.item, mainContainer, theme);

                GuiWidget scope = mainContainer;

                // 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)
                        };

                        bool expanded = true;

                        var sectionState = item as ISectionState;
                        if (sectionState != null)
                        {
                            expanded = sectionState.GetSectionExpansion(sectionStart.Title);
                        }

                        var section = new SectionWidget(sectionStart.Title, column, theme, expanded: expanded);
                        theme.ApplyBoxStyle(section);

                        if (sectionState != null)
                        {
                            section.ExpandedChanged += (s, e) => sectionState.SectionExpansionChanged(sectionStart.Title, e);
                        }

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

                    var editor = CreatePropertyEditor(property, undoBuffer, context, theme);
                    if (editor != null)
                    {
                        scope.AddChild(editor);
                    }
                }

                AddFunctionButtons(item, mainContainer, theme);

                AddWebPageLinkIfRequired(context, mainContainer, theme);

                // add in an Update button if applicable
                if (context.item.GetType().GetCustomAttributes(typeof(ShowUpdateButtonAttribute), true).FirstOrDefault() is ShowUpdateButtonAttribute showUpdate)
                {
                    var updateButton = new TextButton("Update".Localize(), theme)
                    {
                        Margin          = 5,
                        BackgroundColor = theme.MinimalShade,
                        HAnchor         = HAnchor.Right,
                        VAnchor         = VAnchor.Absolute
                    };
                    updateButton.Click += (s, e) =>
                    {
                        context.item.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties));
                    };
                    mainContainer.AddChild(updateButton);
                }

                // Init with custom 'UpdateControls' hooks
                (context.item as IPropertyGridModifier)?.UpdateControls(new PublicPropertyChange(context, "Update_Button"));
            }

            return(mainContainer);
        }
Ejemplo n.º 9
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;

            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);
        }
Ejemplo n.º 10
0
        public GuiWidget Create(IObject3D item, ThemeConfig theme)
        {
            var mainContainer = new FlowLayoutWidget(FlowDirection.TopToBottom)
            {
                HAnchor = HAnchor.Stretch
            };

            // TODO: Long term we should have a solution where editors can extend Draw and Undo without this hack
            var view3DWidget = ApplicationController.Instance.DragDropData.View3DWidget;
            var undoBuffer   = view3DWidget.sceneContext.Scene.UndoBuffer;

            if (item != null)
            {
                var context = new PPEContext()
                {
                    item = item
                };

                // CreateEditor
                AddUnlockLinkIfRequired(context, mainContainer, theme);

                GuiWidget scope = mainContainer;

                // Create a field editor for each editable property detected via reflection
                foreach (var property in GetEditablePropreties(context.item))
                {
                    // 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;
                    }

                    var editor = CreatePropertyEditor(property, undoBuffer, context, theme);
                    if (editor != null)
                    {
                        scope.AddChild(editor);
                    }
                }

                AddWebPageLinkIfRequired(context, mainContainer, theme);

                // add in an Update button if applicable
                var showUpdate = context.item.GetType().GetCustomAttributes(typeof(ShowUpdateButtonAttribute), true).FirstOrDefault() as ShowUpdateButtonAttribute;
                if (showUpdate != null)
                {
                    var updateButton = new TextButton("Update".Localize(), theme)
                    {
                        Margin          = 5,
                        BackgroundColor = theme.MinimalShade,
                        HAnchor         = HAnchor.Right,
                        VAnchor         = VAnchor.Absolute
                    };
                    updateButton.Click += (s, e) =>
                    {
                        context.item.Invalidate(new InvalidateArgs(context.item, InvalidateType.Properties));
                    };
                    mainContainer.AddChild(updateButton);
                }

                // Init with custom 'UpdateControls' hooks
                (context.item as IPropertyGridModifier)?.UpdateControls(new PublicPropertyChange(context, "Update_Button"));
            }

            return(mainContainer);
        }
Ejemplo n.º 11
0
        public static GuiWidget GetFieldContentWithSlider(EditableProperty property, PPEContext context, UIField field, UndoBuffer undoBuffer, Func <string, object> valueFromString, ThemeConfig theme)
        {
            var sliderAttribute = property.PropertyInfo.GetCustomAttributes(true).OfType <SliderAttribute>().FirstOrDefault();

            if (sliderAttribute != null)
            {
                var min   = sliderAttribute.Min;
                var max   = sliderAttribute.Max;
                var delta = max - min;

                // the slider values go from 0 to 1 and are then mapped during translation to the field
                var slider = new Slider(new Vector2(0, 0), 80 * GuiWidget.DeviceScale, 0, 1)
                {
                    VAnchor = VAnchor.Center,
                };

                slider.View.TrackColor  = theme.TextColor;
                slider.View.ThumbColor  = theme.PrimaryAccentColor;
                slider.View.TrackHeight = 1 * GuiWidget.DeviceScale;

                Func <double> getFieldValue = null;

                double GetSlider0To1FromField()
                {
                    return(GetSlider0To1FromValue(getFieldValue()));
                }

                double GetSlider0To1FromValue(double value)
                {
                    var mapped0To1 = Math.Max(0, Math.Min(1, (value - min) / delta));

                    return(Easing.CalculateInverse(sliderAttribute.EasingType, sliderAttribute.EaseOption, mapped0To1));
                }

                double GetFieldFromSlider0To1()
                {
                    var fieldValue = Easing.Calculate(sliderAttribute.EasingType,
                                                      sliderAttribute.EaseOption,
                                                      slider.Value) * delta + min;

                    var snapGridDistance = sliderAttribute.SnapDistance;

                    if (sliderAttribute.UseSnappingGrid)
                    {
                        snapGridDistance = SnapGridDistance();
                    }
                    if (snapGridDistance > 0)
                    {
                        // snap this position to the grid
                        // snap this position to the grid
                        fieldValue = ((int)((fieldValue / snapGridDistance) + .5)) * snapGridDistance;
                    }

                    return(fieldValue);
                }

                double SnapGridDistance()
                {
                    var view3DWidget = slider.Parents <View3DWidget>().FirstOrDefault();

                    if (view3DWidget != null)
                    {
                        var object3DControlLayer = view3DWidget.Descendants <Object3DControlsLayer>().FirstOrDefault();
                        if (object3DControlLayer != null)
                        {
                            return(object3DControlLayer.SnapGridDistance);
                        }
                    }

                    return(0);
                }

                var changeDueToSlider = false;
                var changeDueToField  = false;

                slider.ValueChanged += (s, e) =>
                {
                    if (!changeDueToField)
                    {
                        changeDueToSlider = true;
                        SetValue(property, context, valueFromString, GetFieldFromSlider0To1());
                        changeDueToSlider = false;
                    }
                };

                double sliderDownValue = 0;
                slider.SliderGrabed += (s, e) =>
                {
                    sliderDownValue = getFieldValue();
                };

                slider.SliderReleased += (s, e) =>
                {
                    var currentValue = GetFieldFromSlider0To1();

                    changeDueToSlider = true;
                    SetValue(property, context, valueFromString, currentValue);
                    changeDueToSlider = false;

                    // save the undo information
                    undoBuffer.Add(new UndoRedoActions(() =>
                    {
                        SetValue(property, context, valueFromString, sliderDownValue);
                    },
                                                       () =>
                    {
                        SetValue(property, context, valueFromString, currentValue);
                    }));
                };

                GuiWidget content           = null;
                var       sliderRightMargin = 11;
                if (field is DoubleField doubleField)
                {
                    getFieldValue = () => doubleField.DoubleValue;

                    doubleField.ValueChanged += (s, e) =>
                    {
                        changeDueToField = true;
                        slider.Value     = GetSlider0To1FromField();
                        changeDueToField = false;
                    };

                    content = new FlowLayoutWidget();
                    content.AddChild(slider);
                    content.AddChild(new GuiWidget()
                    {
                        Width  = sliderRightMargin * GuiWidget.DeviceScale,
                        Height = 3
                    });
                    content.AddChild(field.Content);
                }
                else if (field is ExpressionField expressionField)
                {
                    getFieldValue = () =>
                    {
                        if (double.TryParse(expressionField.Value, out double value))
                        {
                            return(value);
                        }

                        return(0);
                    };

                    expressionField.ValueChanged += (s, e) =>
                    {
                        if (!changeDueToSlider)
                        {
                            changeDueToField = true;
                            slider.Value     = GetSlider0To1FromField();
                            changeDueToField = false;
                        }
                    };

                    expressionField.Content.Descendants <TextWidget>().First().TextChanged += (s, e) =>
                    {
                        if (!changeDueToSlider)
                        {
                            changeDueToField = true;
                            var textWidget = expressionField.Content.Descendants <TextWidget>().First();
                            double.TryParse(textWidget.Text, out double textWidgetValue);
                            slider.Value     = GetSlider0To1FromValue(textWidgetValue);
                            changeDueToField = false;
                        }
                    };

                    var leftHold = new FlowLayoutWidget()
                    {
                        HAnchor = HAnchor.Stretch,
                        Margin  = new BorderDouble(0, 0, 66 / GuiWidget.DeviceScale + sliderRightMargin, 0)
                    };
                    leftHold.AddChild(new HorizontalSpacer());
                    leftHold.AddChild(slider);
                    field.Content.AddChild(leftHold, 0);

                    content = field.Content;
                }

                // set the initial value of the slider
                changeDueToField = true;
                slider.Value     = GetSlider0To1FromField();
                changeDueToField = false;

                return(content);
            }

            return(field.Content);
        }