Example #1
0
        public override void Load()
        {
            var library = ApplicationController.Instance.Library;

            long index        = DateTime.Now.Ticks;
            var  libraryItems = new List <GeneratorItem>()
            {
                new GeneratorItem(
                    () => "Cube".Localize(),
                    async() => await CubeObject3D.Create())
                {
                    DateCreated = new System.DateTime(index++)
                },
                new GeneratorItem(
                    () => "Pyramid".Localize(),
                    async() => await PyramidObject3D.Create())
                {
                    DateCreated = new System.DateTime(index++)
                },
                new GeneratorItem(
                    () => "Wedge".Localize(),
                    async() => await WedgeObject3D.Create())
                {
                    DateCreated = new System.DateTime(index++)
                },
                new GeneratorItem(
                    () => "Half Wedge".Localize(),
                    async() => await HalfWedgeObject3D.Create())
                {
                    DateCreated = new System.DateTime(index++)
                },
                new GeneratorItem(
                    () => "Text".Localize(),
                    async() => await TextObject3D.Create())
                {
                    DateCreated = new System.DateTime(index++)
                },

                new GeneratorItem(
                    () => "Cylinder".Localize(),
                    async() => await CylinderObject3D.Create())
                {
                    DateCreated = new System.DateTime(index++)
                },
                new GeneratorItem(
                    () => "Cone".Localize(),
                    async() => await ConeObject3D.Create())
                {
                    DateCreated = new System.DateTime(index++)
                },
                new GeneratorItem(
                    () => "Half Cylinder".Localize(),
                    async() => await HalfCylinderObject3D.Create())
                {
                    DateCreated = new System.DateTime(index++)
                },
                new GeneratorItem(
                    () => "Torus".Localize(),
                    async() => await TorusObject3D.Create())
                {
                    DateCreated = new System.DateTime(index++)
                },
                new GeneratorItem(
                    () => "Ring".Localize(),
                    async() => await RingObject3D.Create())
                {
                    DateCreated = new System.DateTime(index++)
                },
                new GeneratorItem(
                    () => "Sphere".Localize(),
                    async() => await SphereObject3D.Create())
                {
                    DateCreated = new System.DateTime(index++)
                },
                new GeneratorItem(
                    () => "Half Sphere".Localize(),
                    async() => await HalfSphereObject3D.Create())
                {
                    DateCreated = new System.DateTime(index++)
                },
#if DEBUG
                new GeneratorItem(
                    () => "XY Calibration".Localize(),
                    async() => await XyCalibrationFaceObject3D.Create())
                {
                    DateCreated = new System.DateTime(index++)
                },
#endif
                new GeneratorItem(
                    () => "Image Converter".Localize(),
                    () =>
                {
                    // Construct an image
                    var imageObject = new ImageObject3D()
                    {
                        AssetPath = AggContext.StaticData.ToAssetPath(Path.Combine("Images", "mh-logo.png"))
                    };

                    // Construct a scene
                    var bedConfig = new BedConfig(null);
                    var tempScene = bedConfig.Scene;
                    tempScene.Children.Add(imageObject);
                    tempScene.SelectedItem = imageObject;

                    // Invoke ImageConverter operation, passing image and scene
                    SceneOperations.ById("ImageConverter").Action(bedConfig);

                    // Return replacement object constructed in ImageConverter operation
                    var constructedComponent = tempScene.SelectedItem;
                    tempScene.Children.Remove(constructedComponent);

                    return(Task.FromResult <IObject3D>(constructedComponent));
                })
                {
                    DateCreated = new System.DateTime(index++)
                },
                new GeneratorItem(
                    () => "Measure Tool".Localize(),
                    async() => await MeasureToolObject3D.Create())
                {
                    DateCreated = new System.DateTime(index++)
                },
            };

            string title = "Primitive Shapes".Localize();

            foreach (var item in libraryItems)
            {
                item.Category = title;
                Items.Add(item);
            }
        }
        public ViewControls3D(PartWorkspace workspace, ThemeConfig theme, UndoBuffer undoBuffer, bool isPrinterType, bool showPrintButton)
            : base(theme)
        {
            this.theme             = theme;
            this.undoBuffer        = undoBuffer;
            this.ActionArea.Click += (s, e) =>
            {
                view3DWidget.Object3DControlLayer.Focus();
            };

            this.OverflowButton.ToolTipText = "Tool Bar Overflow".Localize();

            this.OverflowButton.DynamicPopupContent = () =>
            {
                bool IncludeInMenu(SceneOperation operation)
                {
                    foreach (var widget in this.ActionArea.Children.Where(c => !c.Visible && !ignoredInMenuTypes.Contains(c.GetType())))
                    {
                        if (operationButtons.TryGetValue(widget, out SceneOperation buttonOperation) &&
                            buttonOperation == operation)
                        {
                            return(true);
                        }
                    }

                    return(false);
                }

                return(SceneOperations.GetToolbarOverflowMenu(AppContext.MenuTheme, sceneContext, IncludeInMenu));
            };

            this.IsPrinterMode = isPrinterType;
            this.sceneContext  = workspace.SceneContext;
            this.workspace     = workspace;

            string iconPath;

            this.AddChild(CreateAddButton(sceneContext, theme));

            this.AddChild(new ToolbarSeparator(theme.GetBorderColor(50), theme.SeparatorMargin));

            bedMenuButton = new PopupMenuButton(StaticData.Instance.LoadIcon("bed.png", 16, 16, theme.InvertIcons), theme)
            {
                Name        = "Bed Options Menu",
                ToolTipText = "Bed",
                Enabled     = true,
                Margin      = theme.ButtonSpacing,
                VAnchor     = VAnchor.Center,
                DrawArrow   = true
            };

            this.AddChild(bedMenuButton);

            this.AddChild(new ToolbarSeparator(theme.GetBorderColor(50), theme.SeparatorMargin));

            this.AddChild(CreateOpenButton(theme));

            this.AddChild(CreateSaveButton(theme));

            this.AddChild(new ToolbarSeparator(theme.GetBorderColor(50), theme.SeparatorMargin));

            undoButton = new IconButton(StaticData.Instance.LoadIcon("Undo_grey_16x.png", 16, 16, theme.InvertIcons), theme)
            {
                Name        = "3D View Undo",
                ToolTipText = "Undo".Localize(),
                Enabled     = false,
                Margin      = theme.ButtonSpacing,
                VAnchor     = VAnchor.Center
            };
            undoButton.Click += (sender, e) =>
            {
                sceneContext.Scene.Undo();
                view3DWidget.Object3DControlLayer.Focus();
            };
            this.AddChild(undoButton);

            redoButton = new IconButton(StaticData.Instance.LoadIcon("Redo_grey_16x.png", 16, 16, theme.InvertIcons), theme)
            {
                Name        = "3D View Redo",
                Margin      = theme.ButtonSpacing,
                ToolTipText = "Redo".Localize(),
                Enabled     = false,
                VAnchor     = VAnchor.Center
            };
            redoButton.Click += (sender, e) =>
            {
                sceneContext.Scene.Redo();
                view3DWidget.Object3DControlLayer.Focus();
            };
            this.AddChild(redoButton);

            if (showPrintButton)
            {
                var printButton = new TextButton("Print", theme)
                {
                    Name            = "Print Button",
                    BackgroundColor = theme.AccentMimimalOverlay
                };
                printButton.Click += (s, e) =>
                {
                    view3DWidget.PushToPrinterAndPrint();
                };
                this.AddChild(printButton);
            }

            this.AddChild(new ToolbarSeparator(theme.GetBorderColor(50), theme.SeparatorMargin));

            undoButton.Enabled = undoBuffer.UndoCount > 0;
            redoButton.Enabled = undoBuffer.RedoCount > 0;

            var buttonGroupA = new ObservableCollection <GuiWidget>();

            if (UserSettings.Instance.IsTouchScreen)
            {
                iconPath     = Path.Combine("ViewTransformControls", "rotate.png");
                rotateButton = new RadioIconButton(StaticData.Instance.LoadIcon(iconPath, 32, 32, theme.InvertIcons), theme)
                {
                    SiblingRadioButtonList = buttonGroupA,
                    ToolTipText            = "Rotate (Alt + Left Mouse)".Localize(),
                    Margin = theme.ButtonSpacing
                };
                rotateButton.Click += (s, e) => this.ActiveButton = ViewControls3DButtons.Rotate;
                buttonGroupA.Add(rotateButton);
                AddChild(rotateButton);

                iconPath        = Path.Combine("ViewTransformControls", "translate.png");
                translateButton = new RadioIconButton(StaticData.Instance.LoadIcon(iconPath, 32, 32, theme.InvertIcons), theme)
                {
                    SiblingRadioButtonList = buttonGroupA,
                    ToolTipText            = "Move (Shift + Left Mouse)".Localize(),
                    Margin = theme.ButtonSpacing
                };
                translateButton.Click += (s, e) => this.ActiveButton = ViewControls3DButtons.Translate;
                buttonGroupA.Add(translateButton);
                AddChild(translateButton);

                iconPath    = Path.Combine("ViewTransformControls", "scale.png");
                scaleButton = new RadioIconButton(StaticData.Instance.LoadIcon(iconPath, 32, 32, theme.InvertIcons), theme)
                {
                    SiblingRadioButtonList = buttonGroupA,
                    ToolTipText            = "Zoom (Ctrl + Left Mouse)".Localize(),
                    Margin = theme.ButtonSpacing
                };
                scaleButton.Click += (s, e) => this.ActiveButton = ViewControls3DButtons.Scale;
                buttonGroupA.Add(scaleButton);
                AddChild(scaleButton);

                rotateButton.Checked = true;

                // Add vertical separator
                this.AddChild(new ToolbarSeparator(theme.GetBorderColor(50), theme.SeparatorMargin));

                iconPath         = Path.Combine("ViewTransformControls", "partSelect.png");
                partSelectButton = new RadioIconButton(StaticData.Instance.LoadIcon(iconPath, 32, 32, theme.InvertIcons), theme)
                {
                    SiblingRadioButtonList = buttonGroupA,
                    ToolTipText            = "Select Part".Localize(),
                    Margin = theme.ButtonSpacing
                };
                partSelectButton.Click += (s, e) => this.ActiveButton = ViewControls3DButtons.PartSelect;
                buttonGroupA.Add(partSelectButton);
                AddChild(partSelectButton);
            }

            operationButtons = new Dictionary <GuiWidget, SceneOperation>();

            // Add Selected IObject3D -> Operations to toolbar
            foreach (var namedAction in SceneOperations.All)
            {
                if (namedAction is SceneSelectionSeparator)
                {
                    this.AddChild(new ToolbarSeparator(theme.GetBorderColor(50), theme.SeparatorMargin));
                    continue;
                }

                // add the create support before the align
                if (namedAction is OperationGroup group &&
                    group.Id == "Transform")
                {
                    this.AddChild(CreateSupportButton(theme));
                    this.AddChild(new ToolbarSeparator(theme.GetBorderColor(50), theme.SeparatorMargin));
                }

                GuiWidget button = null;

                if (namedAction is OperationGroup operationGroup)
                {
                    if (operationGroup.Collapse)
                    {
                        var defaultOperation = operationGroup.GetDefaultOperation();

                        PopupMenuButton groupButton = null;

                        groupButton = theme.CreateSplitButton(
                            new SplitButtonParams()
                        {
                            Icon          = defaultOperation.Icon(theme.InvertIcons),
                            DefaultAction = (menuButton) =>
                            {
                                defaultOperation.Action.Invoke(sceneContext);
                            },
                            DefaultActionTooltip = defaultOperation.HelpText ?? defaultOperation.Title,
                            ButtonName           = defaultOperation.Title,
                            ExtendPopupMenu      = (PopupMenu popupMenu) =>
                            {
                                foreach (var operation in operationGroup.Operations)
                                {
                                    var operationMenu = popupMenu.CreateMenuItem(operation.Title, operation.Icon?.Invoke(theme.InvertIcons));

                                    operationMenu.Enabled     = operation.IsEnabled(sceneContext);
                                    operationMenu.ToolTipText = operation.Title;

                                    if (!operationMenu.Enabled &&
                                        !string.IsNullOrEmpty(operation.HelpText))
                                    {
                                        operationMenu.ToolTipText += "\n\n" + operation.HelpText;
                                    }

                                    operationMenu.Click += (s, e) => UiThread.RunOnIdle(() =>
                                    {
                                        if (defaultOperation != operation)
                                        {
                                            // Update button
                                            var iconButton = groupButton.Children.OfType <IconButton>().First();
                                            iconButton.SetIcon(operation.Icon(theme.InvertIcons));
                                            iconButton.ToolTipText = operation.HelpText ?? operation.Title;

                                            UserSettings.Instance.set(operationGroup.GroupRecordId, operationGroup.Operations.IndexOf(operation).ToString());

                                            defaultOperation = operation;

                                            iconButton.Invalidate();
                                        }

                                        operation.Action?.Invoke(sceneContext);
                                    });
                                }
                            }
                        },
                            operationGroup);

                        button = groupButton;
                    }
                    else
                    {
                        if (!(this.ActionArea.Children.LastOrDefault() is ToolbarSeparator))
                        {
                            this.AddChild(new ToolbarSeparator(theme.GetBorderColor(50), theme.SeparatorMargin));
                        }

                        foreach (var operation in operationGroup.Operations)
                        {
                            var operationButton = new OperationIconButton(operation, sceneContext, theme);
                            operationButtons.Add(operationButton, operation);

                            this.AddChild(operationButton);
                        }

                        this.AddChild(new ToolbarSeparator(theme.GetBorderColor(50), theme.SeparatorMargin));
                    }
                }
                else if (namedAction.Icon != null)
                {
                    button = new IconButton(namedAction.Icon(theme.InvertIcons), theme)
                    {
                        Name            = namedAction.Title + " Button",
                        ToolTipText     = namedAction.Title,
                        Margin          = theme.ButtonSpacing,
                        BackgroundColor = theme.ToolbarButtonBackground,
                        HoverColor      = theme.ToolbarButtonHover,
                        MouseDownColor  = theme.ToolbarButtonDown,
                    };
                }
                else
                {
                    button = new TextButton(namedAction.Title, theme)
                    {
                        Name            = namedAction.Title + " Button",
                        Margin          = theme.ButtonSpacing,
                        BackgroundColor = theme.ToolbarButtonBackground,
                        HoverColor      = theme.ToolbarButtonHover,
                        MouseDownColor  = theme.ToolbarButtonDown,
                    };
                }

                if (button != null)
                {
                    operationButtons.Add(button, namedAction);

                    // Only bind Click event if not a SplitButton
                    if (!(button is PopupMenuButton))
                    {
                        button.Click += (s, e) => UiThread.RunOnIdle(() =>
                        {
                            namedAction.Action.Invoke(sceneContext);
                            var partTab = button.Parents <PartTabPage>().FirstOrDefault();
                            var view3D  = partTab.Descendants <View3DWidget>().FirstOrDefault();
                            view3D.Object3DControlLayer.Focus();
                        });
                    }

                    this.AddChild(button);
                }
            }

            // Register listeners
            undoBuffer.Changed += UndoBuffer_Changed;
            sceneContext.Scene.SelectionChanged += UpdateToolbarButtons;
            sceneContext.Scene.ItemsModified    += UpdateToolbarButtons;

            // Run on load
            UpdateToolbarButtons(null, null);
        }
        public override void Load()
        {
            var library = ApplicationController.Instance.Library;

            long index        = DateTime.Now.Ticks;
            var  libraryItems = new List <GeneratorItem>()
            {
                new GeneratorItem(
                    () => "Cube".Localize(),
                    async() => await CubeObject3D.Create())
                {
                    DateCreated = new DateTime(index++)
                },
                new GeneratorItem(
                    () => "Pyramid".Localize(),
                    async() => await PyramidObject3D.Create())
                {
                    DateCreated = new DateTime(index++)
                },
                new GeneratorItem(
                    () => "Wedge".Localize(),
                    async() => await WedgeObject3D.Create())
                {
                    DateCreated = new DateTime(index++)
                },
                new GeneratorItem(
                    () => "Half Wedge".Localize(),
                    async() => await HalfWedgeObject3D.Create())
                {
                    DateCreated = new DateTime(index++)
                },
                new GeneratorItem(
                    () => "Text".Localize(),
                    async() => await TextObject3D.Create())
                {
                    DateCreated = new DateTime(index++)
                },
                new GeneratorItem(
                    () => "Cylinder".Localize(),
                    async() => await CylinderObject3D.Create())
                {
                    DateCreated = new DateTime(index++)
                },
                new GeneratorItem(
                    () => "Cone".Localize(),
                    async() => await ConeObject3D.Create())
                {
                    DateCreated = new DateTime(index++)
                },
                new GeneratorItem(
                    () => "Half Cylinder".Localize(),
                    async() => await HalfCylinderObject3D.Create())
                {
                    DateCreated = new DateTime(index++)
                },
                new GeneratorItem(
                    () => "Torus".Localize(),
                    async() => await TorusObject3D.Create())
                {
                    DateCreated = new DateTime(index++)
                },
                new GeneratorItem(
                    () => "Ring".Localize(),
                    async() => await RingObject3D.Create())
                {
                    DateCreated = new DateTime(index++)
                },
                new GeneratorItem(
                    () => "Sphere".Localize(),
                    async() => await SphereObject3D.Create())
                {
                    DateCreated = new DateTime(index++)
                },
                new GeneratorItem(
                    () => "Half Sphere".Localize(),
                    async() => await HalfSphereObject3D.Create())
                {
                    DateCreated = new DateTime(index++)
                },
#if DEBUG
                new GeneratorItem(
                    () => "SCAD Script".Localize(),
                    async() => await OpenScadScriptObject3D.Create())
                {
                    DateCreated = new DateTime(index++)
                },
                new GeneratorItem(
                    () => "MarchingSquares".Localize(),
                    async() => await MarchingSquaresObject3D.Create())
                {
                    DateCreated = new DateTime(index++)
                },
#endif
                new GeneratorItem(
                    () => "Image Converter".Localize(),
                    () =>
                {
                    // Construct an image
                    var imageObject = new ImageObject3D()
                    {
                        AssetPath = StaticData.Instance.ToAssetPath(Path.Combine("Images", "mh-logo.png"))
                    };

                    // Construct a scene
                    var bedConfig = new BedConfig(null);
                    var tempScene = bedConfig.Scene;
                    tempScene.Children.Add(imageObject);
                    tempScene.SelectedItem = imageObject;

                    // Invoke ImageConverter operation, passing image and scene
                    SceneOperations.ById("ImageConverter").Action(bedConfig);

                    // Return replacement object constructed in ImageConverter operation
                    var constructedComponent = tempScene.SelectedItem;
                    tempScene.Children.Remove(constructedComponent);

                    return(Task.FromResult(constructedComponent));
                })
                {
                    DateCreated = new DateTime(index++)
                },
                new GeneratorItem(
                    () => "Measure Tool".Localize(),
                    async() => await MeasureToolObject3D.Create())
                {
                    DateCreated = new DateTime(index++)
                },
                new GeneratorItem(
                    () => "Description".Localize(),
                    async() => await DescriptionObject3D.Create())
                {
                    DateCreated = new DateTime(index++)
                },
                new GeneratorItem(
                    () => "Variable Sheet".Localize(),
                    async() => await SheetObject3D.Create())
                {
                    DateCreated = new DateTime(index++)
                },
            };

            string title = "Primitive Shapes".Localize();

            foreach (var item in libraryItems)
            {
                item.Category = title;
                Items.Add(item);
            }

            this.ChildContainers.Add(
                new DynamicContainerLink(
                    () => "Primitives 2D".Localize(),
                    StaticData.Instance.LoadIcon(Path.Combine("Library", "folder.png")),
                    StaticData.Instance.LoadIcon(Path.Combine("Library", "primitives_library_icon.png")),
                    () => new Primitives2DContainer())
            {
                IsReadOnly = true
            });
        }
Example #4
0
        private static TreeNode AddItem(IObject3D item, string itemName, TreeNode parentNode, Dictionary <IObject3D, TreeNode> keyValues, ThemeConfig theme)
        {
            if (item is InsertionGroupObject3D insertionGroup)
            {
                return(new TreeNode(theme)
                {
                    Text = "Loading".Localize(),
                    Tag = item,
                    TextColor = theme.TextColor,
                    PointSize = theme.DefaultFontSize,
                });
            }

            var node = new TreeNode(theme)
            {
                Text      = itemName,
                Tag       = item,
                TextColor = theme.TextColor,
                PointSize = theme.DefaultFontSize,
            };

            if (!keyValues.ContainsKey(item))
            {
                keyValues.Add(item, node);
            }

            // Check for operation resulting in the given type
            var image = SceneOperations.GetIcon(item.GetType(), theme);

            if (image != null)
            {
                node.Image = image;
            }
            else
            {
                node.Image = ApplicationController.Instance.Thumbnails.DefaultThumbnail();

                node.Load += (s, e) =>
                {
                    string contentID = item.MeshRenderId().ToString();
                    if (item is IStaticThumbnail staticThumbnail)
                    {
                        contentID = $"MatterHackers/ItemGenerator/{staticThumbnail.ThumbnailName}".GetLongHashCode().ToString();
                    }

                    var thumbnail = ApplicationController.Instance.Thumbnails.LoadCachedImage(contentID, 16, 16);

                    node.Image = thumbnail ?? ApplicationController.Instance.Thumbnails.DefaultThumbnail();
                };
            }

            if (parentNode != null)
            {
                parentNode.Nodes.Add(node);
                if (parentNode.Tag is IObject3D object3D)
                {
                    parentNode.Expanded = object3D.Expanded;
                }
            }

            node.ExpandedChanged += (s, e) =>
            {
                if (item is Object3D object3D)
                {
                    object3D.Expanded = node.Expanded;
                }
            };

            return(node);
        }
        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 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;
                }
            }
 private void Awake()
 {
     Instance = this;
 }