diff --git a/CustomWidgets/TreeView/TreeNode.cs b/CustomWidgets/TreeView/TreeNode.cs new file mode 100644 index 000000000..ad436b789 --- /dev/null +++ b/CustomWidgets/TreeView/TreeNode.cs @@ -0,0 +1,391 @@ +/* +Copyright (c) 2016, Lars Brubaker, John Lewin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the FreeBSD Project. +*/ + +using MatterHackers.Agg; +using MatterHackers.Agg.Font; +using MatterHackers.Agg.Image; +using MatterHackers.Agg.UI; +using System; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; + +namespace MatterHackers.MatterControl.CustomWidgets.TreeView +{ + public class TreeNode : FlowLayoutWidget, ICheckbox + { + private GuiWidget content; + + public TreeNode() + : base(FlowDirection.TopToBottom) + { + HAnchor = HAnchor.Fit | HAnchor.Left; + VAnchor = VAnchor.Fit; + + TitleBar = new FlowLayoutWidget(); + TitleBar.Click += (s, e) => + { + if (TreeView != null) + { + TreeView.SelectedNode = this; + } + }; + AddChild(TitleBar); + RebuildTitleBar(); + + content = new FlowLayoutWidget(FlowDirection.TopToBottom) + { + HAnchor = HAnchor.Fit | HAnchor.Left, + Visible = false, // content starts out not visible + Name = "content", + Margin = new BorderDouble(25, 3), + }; + AddChild(content); + + Nodes.CollectionChanged += Nodes_CollectionChanged; + } + + public FlowLayoutWidget TitleBar { get; } + + public void BeginEdit() + { + throw new NotImplementedException(); + } + + public void Collapse(bool collapseChildren) + { + throw new NotImplementedException(); + } + + public void Collapse() + { + throw new NotImplementedException(); + } + + public void EndEdit(bool cancel) + { + throw new NotImplementedException(); + } + + public void EnsureVisible() + { + throw new NotImplementedException(); + } + + public void ExpandAll() + { + throw new NotImplementedException(); + } + + public int GetNodeCount(bool includeSubTrees) + { + if (includeSubTrees) + { + return this.Descendants().Count(); + } + + return content.Children.Where((c) => c is TreeNode).Count(); + } + + public override void OnTextChanged(EventArgs e) + { + RebuildTitleBar(); + base.OnTextChanged(e); + } + + public void Remove() + { + throw new NotImplementedException(); + } + + public void Toggle() + { + content.Visible = !content.Visible; + } + + private void Nodes_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + RebuildContentSection(); + } + + private void RebuildContentSection() + { + // If the node count is starting at 0 we are adding content and need to rebuild the title bar so it will have a + in it + bool needToRebuildTitleBar = GetNodeCount(false) == 0; + + // Remove but don't close all the current nodes + content.RemoveAllChildren(); + + // Then add them back in (after the change) + foreach (var node in Nodes) + { + node.NodeParent = this; + node.ClearRemovedFlag(); + content.AddChild(node); + } + + // If the node count is ending at 0 we removed content and need to rebuild the title bar so it will net have a + in it + needToRebuildTitleBar |= GetNodeCount(false) == 0; + if (needToRebuildTitleBar) + { + RebuildTitleBar(); + } + } + + private void RebuildTitleBar() + { + TitleBar.RemoveAllChildren(); + if (content != null + && GetNodeCount(false) > 0) + { + // add a check box + var expandCheckBox = new CheckBox("") + { + Checked = Expanded, + VAnchor = VAnchor.Center + }; + ExpandedChanged += (s, e) => + { + expandCheckBox.Checked = Expanded; + }; + expandCheckBox.CheckedStateChanged += (s, e) => + { + Expanded = expandCheckBox.Checked; + }; + TitleBar.AddChild(expandCheckBox); + } + // add a check box + if (Image != null) + { + TitleBar.AddChild(new ImageWidget(Image) + { + VAnchor = VAnchor.Center, + //BorderColor = new Color(ActiveTheme.Instance.PrimaryTextColor, 20), + BackgroundColor = new Color(ActiveTheme.Instance.PrimaryTextColor, 12), + Border = 1, + Margin = 2, + }); + }; + TitleBar.AddChild(new TextWidget(Text) + { + Selectable = false + }); + } + + #region Properties + + private ImageBuffer _image = new ImageBuffer(16, 16); + + public bool Checked { get; set; } + + public bool Editing { get; } + + public bool Expanded + { + get + { + return content.Visible; + } + set + { + if (content.Visible != value) + { + content.Visible = value; + ExpandedChanged?.Invoke(this, null); + } + } + } + + public TreeNode FirstNode { get; } + + public ImageBuffer Image + { + get + { + return _image; + } + + set + { + if (_image != value) + { + _image = value; + OnImageChanged(null); + } + } + } + + public TreeNode LastNode { get; } + + /// + /// Gets the zero-based depth of the tree node in the TreeView control. + /// + public int Level { get; } + + // + // Summary: + // Gets the next sibling tree node. + // + // Returns: + // A TreeNode that represents the next sibling tree node. + public TreeNode NextNode { get; } + + // + // Summary: + // Gets the next visible tree node. + // + // Returns: + // A TreeNode that represents the next visible tree node. + public TreeNode NextVisibleNode { get; } + + // + // Summary: + // Gets or sets the font that is used to display the text on the tree node label. + // + // Returns: + // The StyledTypeFace that is used to display the text on the tree node label. + public StyledTypeFace NodeFont { get; set; } + + // + // Summary: + // Gets the parent tree node of the current tree node. + // + // Returns: + // A TreeNode that represents the parent of the current tree + // node. + public TreeNode NodeParent { get; protected set; } + + public ObservableCollection Nodes { get; } = new ObservableCollection(); + + public int PointSize { get; set; } + + // + // Summary: + // Gets the previous sibling tree node. + // + // Returns: + // A TreeNode that represents the previous sibling tree node. + public TreeNode PrevNode { get; } + + // + // Summary: + // Gets the previous visible tree node. + // + // Returns: + // A TreeNode that represents the previous visible tree node. + public TreeNode PrevVisibleNode { get; } + + // + // Summary: + // Gets a value indicating whether the tree node is in the selected state. + // + // Returns: + // true if the tree node is in the selected state; otherwise, false. + public bool Selected + { + get + { + if (TreeView != null) + { + return TreeView.SelectedNode == this; + } + + return false; + } + } + + // + // Summary: + // Gets or sets the image list index value of the image that is displayed when the + // tree node is in the selected state. + // + // Returns: + // A zero-based index value that represents the image position in an ImageList. + public ImageBuffer SelectedImage { get; set; } + + // + // Summary: + // Gets or sets the index of the image that is used to indicate the state of the + // TreeNode when the parent TreeView has + // its TreeView.CheckBoxes property set to false. + // + // Returns: + // The index of the image that is used to indicate the state of the TreeNode. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // The specified index is less than -1 or greater than 14. + public ImageBuffer StateImage { get; set; } + + // + // Summary: + // Gets or sets the object that contains data about the tree node. + // + // Returns: + // An System.Object that contains data about the tree node. The default is null. + public object Tag { get; set; } + + public Color TextColor { get; set; } + + // + // Summary: + // Gets the parent tree view that the tree node is assigned to. + // + // Returns: + // A TreeView that represents the parent tree view that the + // tree node is assigned to, or null if the node has not been assigned to a tree + // view. + public virtual TreeView TreeView + { + get + { + return NodeParent.TreeView; + } + } + + private void OnImageChanged(EventArgs args) + { + ImageChanged?.Invoke(this, null); + + RebuildTitleBar(); + } + + #endregion Properties + + #region Events + + public event EventHandler CheckedStateChanged; + + public event EventHandler ExpandedChanged; + + public event EventHandler ImageChanged; + + #endregion Events + } +} \ No newline at end of file diff --git a/CustomWidgets/TreeView/TreeView.cs b/CustomWidgets/TreeView/TreeView.cs new file mode 100644 index 000000000..7c5194cc0 --- /dev/null +++ b/CustomWidgets/TreeView/TreeView.cs @@ -0,0 +1,370 @@ +/* +Copyright (c) 2016, Lars Brubaker, John Lewin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the FreeBSD Project. +*/ + +using MatterHackers.Agg; +using MatterHackers.Agg.Image; +using MatterHackers.Agg.UI; +using MatterHackers.VectorMath; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace MatterHackers.MatterControl.CustomWidgets.TreeView +{ + public class TopNode : TreeNode + { + internal TreeView treeView; + public override TreeView TreeView => treeView; + } + + public class TreeView : ScrollableWidget + { + public TreeView(TopNode topNode) + : this(topNode, 0, 0) + { + } + + public TreeView(TopNode topNode, int width, int height) + : base(width, height) + { + AutoScroll = true; + + topNode.treeView = this; + TopNode = topNode; + HAnchor = HAnchor.Stretch; + VAnchor = VAnchor.Stretch; + + AddChild(TopNode); + } + + #region Events + + public event EventHandler AfterCheck; + + public event EventHandler AfterCollapse; + + public event EventHandler AfterExpand; + + public event EventHandler AfterLabelEdit; + + public event EventHandler AfterSelect; + + public event EventHandler BeforeCheck; + + public event EventHandler BeforeCollapse; + + public event EventHandler BeforeExpand; + + public event EventHandler BeforeLabelEdit; + + public event EventHandler BeforeSelect; + + public event EventHandler NodeMouseClick; + + public event EventHandler NodeMouseDoubleClick; + + public event EventHandler NodeMouseHover; + + #endregion Events + + #region Properties + + // + // Summary: + // Gets or sets a value indicating whether check boxes are displayed next to the + // tree nodes in the tree view control. + // + // Returns: + // true if a check box is displayed next to each tree node in the tree view control; + // otherwise, false. The default is false. + public bool CheckBoxes { get; set; } + + // + // Summary: + // Gets or sets a value indicating whether the selection highlight spans the width + // of the tree view control. + // + // Returns: + // true if the selection highlight spans the width of the tree view control; otherwise, + // false. The default is false. + public bool FullRowSelect { get; set; } + + // + // Summary: + // Gets or sets a value indicating whether the selected tree node remains highlighted + // even when the tree view has lost the focus. + // + // Returns: + // true if the selected tree node is not highlighted when the tree view has lost + // the focus; otherwise, false. The default is true. + public bool HideSelection { get; set; } + + // + // Summary: + // Gets or sets the distance to indent each child tree node level. + // + // Returns: + // The distance, in pixels, to indent each child tree node level. The default value + // is 19. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // The assigned value is less than 0 (see Remarks).-or- The assigned value is greater + // than 32,000. + public int Indent { get; set; } + + // + // Summary: + // Gets or sets the height of each tree node in the tree view control. + // + // Returns: + // The height, in pixels, of each tree node in the tree view. + // + // Exceptions: + // T:System.ArgumentOutOfRangeException: + // The assigned value is less than one.-or- The assigned value is greater than the + // System.Int16.MaxValue value. + public int ItemHeight { get; set; } + + // + // Summary: + // Gets or sets a value indicating whether the label text of the tree nodes can + // be edited. + // + // Returns: + // true if the label text of the tree nodes can be edited; otherwise, false. The + // default is false. + public bool LabelEdit { get; set; } + + // + // Summary: + // Gets or sets the color of the lines connecting the nodes of the TreeView + // control. + // + // Returns: + // The System.Drawing.Color of the lines connecting the tree nodes. + public Color LineColor { get; set; } + + // + // Summary: + // Gets or sets the delimiter string that the tree node path uses. + // + // Returns: + // The delimiter string that the tree node TreeNode.FullPath + // property uses. The default is the backslash character (\). + public string PathSeparator { get; set; } + + public Color TextColor { get; set; } = Color.Black; + + public double PointSize { get; set; } = 12; + + // + // Summary: + // Gets or sets a value indicating whether the tree view control displays scroll + // bars when they are needed. + // + // Returns: + // true if the tree view control displays scroll bars when they are needed; otherwise, + // false. The default is true. + public bool Scrollable { get; set; } + + // + // Summary: + // Gets or sets the tree node that is currently selected in the tree view control. + // + // Returns: + // The TreeNode that is currently selected in the tree view + // control. + TreeNode _selectedNode; + public TreeNode SelectedNode + { + get => _selectedNode; set + { + if (value != _selectedNode) + { + OnBeforeSelect(null); + foreach (var node in this.Descendants().Where((c) => c != value)) + { + node.TitleBar.BackgroundColor = Color.Transparent; + } + _selectedNode = value; + _selectedNode.TitleBar.BackgroundColor = Color.Red; + OnAfterSelect(null); + } + } + } + + public bool ShowLines { get; set; } + public bool ShowNodeToolTips { get; set; } + public bool ShowPlusMinus { get; set; } + public bool ShowRootLines { get; set; } + public bool Sorted { get; set; } + public TreeNode TopNode { get; } + + public IComparer TreeViewNodeSorter { get; set; } + + // + // Summary: + // Gets the number of tree nodes that can be fully visible in the tree view control. + // + // Returns: + // The number of TreeNode items that can be fully visible in + // the TreeView control. + public int VisibleCount { get; } + + #endregion Properties + + // + // Summary: + // Disables any redrawing of the tree view. + public void BeginUpdate() + { + throw new NotImplementedException(); + } + + // + // Summary: + // Collapses all the tree nodes. + public void CollapseAll() + { + throw new NotImplementedException(); + } + + // + // Summary: + // Enables the redrawing of the tree view. + public void EndUpdate() + { + throw new NotImplementedException(); + } + + // + // Summary: + // Expands all the tree nodes. + public void ExpandAll() + { + throw new NotImplementedException(); + } + + // + // Summary: + // Retrieves the tree node that is at the specified point. + // + // Parameters: + // pt: + // The System.Drawing.Point to evaluate and retrieve the node from. + // + // Returns: + // The TreeNode at the specified point, in tree view (client) + // coordinates, or null if there is no node at that location. + public TreeNode GetNodeAt(Vector2 pt) + { + throw new NotImplementedException(); + } + + /// + /// Retrieves the number of tree nodes, optionally including those in all subtrees, + /// assigned to the tree view control. + /// + /// true to count the TreeNode items that the subtrees contain; + /// otherwise, false. + /// The number of tree nodes, optionally including those in all subtrees, assigned + /// to the tree view control. + public int GetNodeCount(bool includeSubTrees) + { + throw new NotImplementedException(); + } + + public void Sort() + { + throw new NotImplementedException(); + } + + protected internal virtual void OnAfterCollapse(EventArgs e) + { + throw new NotImplementedException(); + } + + protected internal virtual void OnBeforeCollapse(EventArgs e) + { + throw new NotImplementedException(); + } + + protected virtual void OnAfterCheck(EventArgs e) + { + throw new NotImplementedException(); + } + + protected virtual void OnAfterExpand(EventArgs e) + { + throw new NotImplementedException(); + } + + protected virtual void OnAfterLabelEdit(EventArgs e) + { + throw new NotImplementedException(); + } + + protected virtual void OnAfterSelect(TreeNode e) + { + AfterSelect?.Invoke(this, e); + } + + protected virtual void OnBeforeCheck(EventArgs e) + { + throw new NotImplementedException(); + } + + protected virtual void OnBeforeExpand(EventArgs e) + { + throw new NotImplementedException(); + } + + protected virtual void OnBeforeLabelEdit(EventArgs e) + { + throw new NotImplementedException(); + } + + protected virtual void OnBeforeSelect(TreeNode e) + { + BeforeSelect?.Invoke(this, e); + } + + protected virtual void OnNodeMouseClick(EventArgs e) + { + throw new NotImplementedException(); + } + + protected virtual void OnNodeMouseDoubleClick(EventArgs e) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Library/Widgets/ListView/ListViewItemBase.cs b/Library/Widgets/ListView/ListViewItemBase.cs index 47b4b4cf2..3b1936e25 100644 --- a/Library/Widgets/ListView/ListViewItemBase.cs +++ b/Library/Widgets/ListView/ListViewItemBase.cs @@ -65,32 +65,6 @@ namespace MatterHackers.MatterControl.CustomWidgets this.thumbHeight = height; } - // TODO: Why is this static? - private static bool WidgetOnScreen(GuiWidget widget, RectangleDouble bounds) - { - if (!widget.Visible) - { - return false; - } - else - { - if (widget.Parent != null) - { - var boundsInParentSpace = widget.TransformToParentSpace(widget.Parent, bounds); - var intersects = boundsInParentSpace.IntersectRectangles(boundsInParentSpace, widget.Parent.LocalBounds); - if (!intersects - || boundsInParentSpace.Width <= 0 - || boundsInParentSpace.Height <= 0 - || !WidgetOnScreen(widget.Parent, boundsInParentSpace)) - { - return false; - } - } - } - - return true; - } - public Task LoadItemThumbnail() { return LoadItemThumbnail( @@ -99,7 +73,7 @@ namespace MatterHackers.MatterControl.CustomWidgets this.thumbWidth, this.thumbHeight, this.SetItemThumbnail, - () => ListViewItemBase.WidgetOnScreen(this, this.LocalBounds)); + () => this.ActuallyVisibleOnScreen()); } private static async Task LoadItemThumbnail(ILibraryItem libraryItem, ILibraryContainer libraryContainer, int thumbWidth, int thumbHeight, Action thumbnailSetter, Func shouldGenerateThumbnail) diff --git a/MatterControl.csproj b/MatterControl.csproj index 9b8f85bd7..98575c6fc 100644 --- a/MatterControl.csproj +++ b/MatterControl.csproj @@ -77,6 +77,8 @@ + + diff --git a/PartPreviewWindow/SelectedObjectPanel.cs b/PartPreviewWindow/SelectedObjectPanel.cs index c7f571cf2..281491128 100644 --- a/PartPreviewWindow/SelectedObjectPanel.cs +++ b/PartPreviewWindow/SelectedObjectPanel.cs @@ -33,20 +33,20 @@ using System.Linq; using MatterHackers.Agg; using MatterHackers.Agg.Platform; using MatterHackers.Agg.UI; -using MatterHackers.Agg.UI.TreeView; using MatterHackers.DataConverters3D; using MatterHackers.Localizations; using MatterHackers.MatterControl.CustomWidgets; using MatterHackers.MatterControl.DesignTools; using MatterHackers.MatterControl.Library; using MatterHackers.VectorMath; +using MatterHackers.MatterControl.CustomWidgets.TreeView; namespace MatterHackers.MatterControl.PartPreviewWindow { [HideUpdateButtonAttribute] public class SelectedObjectPanel : FlowLayoutWidget, IContentStore { - private IObject3D item = new Object3D(); + private IObject3D rootSelectedItem = new Object3D(); private ThemeConfig theme; private View3DWidget view3DWidget; @@ -77,9 +77,9 @@ namespace MatterHackers.MatterControl.PartPreviewWindow }); inlineTitleEdit.TitleChanged += (s, e) => { - if (item != null) + if (rootSelectedItem != null) { - item.Name = inlineTitleEdit.Text; + rootSelectedItem.Name = inlineTitleEdit.Text; } }; @@ -131,7 +131,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow bed, theme); - var clonedItem = this.item.Clone(); + var clonedItem = this.rootSelectedItem.Clone(); // Edit in Identity transform clonedItem.Matrix = Matrix4X4.Identity; @@ -143,16 +143,16 @@ namespace MatterHackers.MatterControl.PartPreviewWindow { var replacement = object3D.Clone(); - this.item.Parent.Children.Modify(list => + this.rootSelectedItem.Parent.Children.Modify(list => { - list.Remove(item); + list.Remove(rootSelectedItem); // Restore matrix of item being replaced - replacement.Matrix = item.Matrix; + replacement.Matrix = rootSelectedItem.Matrix; list.Add(replacement); - item = replacement; + rootSelectedItem = replacement; }); scene.SelectedItem = replacement; @@ -172,7 +172,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow applyButton.Click += (s, e) => { scene.SelectedItem = null; - this.item.Apply(view3DWidget.Scene.UndoBuffer); + this.rootSelectedItem.Apply(view3DWidget.Scene.UndoBuffer); }; scene.SelectionChanged += (s, e) => applyButton.Enabled = scene.SelectedItem?.CanApply == true; toolbar.AddChild(applyButton); @@ -186,7 +186,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow removeButton.Click += (s, e) => { scene.SelectedItem = null; - this.item.Remove(view3DWidget.Scene.UndoBuffer); + this.rootSelectedItem.Remove(view3DWidget.Scene.UndoBuffer); }; scene.SelectionChanged += (s, e) => removeButton.Enabled = scene.SelectedItem?.CanRemove == true; toolbar.AddChild(removeButton); @@ -260,29 +260,11 @@ namespace MatterHackers.MatterControl.PartPreviewWindow inlineTitleEdit.Text = selectedItem.Name ?? selectedItemType.Name; - this.item = selectedItem; + this.rootSelectedItem = selectedItem; var viewMode = printer?.ViewState.ViewMode; - HashSet mappedEditors = ApplicationController.Instance.GetEditorsForType(selectedItemType); - - var activeEditors = new List<(IObject3DEditor, IObject3D, string)>(); - - foreach (var child in selectedItem.DescendantsAndSelf()) - { - if (ApplicationController.Instance.GetEditorsForType(child.GetType())?.FirstOrDefault() is IObject3DEditor editor) - { - activeEditors.Add((editor, child, child.Name)); - } - - // If the object is not persistable than don't show its details. - if(!child.Persistable) - { - break; - } - } - - ShowObjectEditor(activeEditors, selectedItem); + ShowObjectEditor(selectedItem); } private class OperationButton :TextButton @@ -303,107 +285,118 @@ namespace MatterHackers.MatterControl.PartPreviewWindow } } - private void ShowObjectEditor(IEnumerable<(IObject3DEditor editor, IObject3D item, string displayName)> scope, IObject3D rootSelection) + private void ShowObjectEditor(IObject3D selectedItem) { editorPanel.CloseAllChildren(); - if (scope == null) + // Add the tree view container. eventually we may want to make this a stretch container of some type + var treeViewContainer = new GuiWidget() { - return; - } + HAnchor = HAnchor.Stretch, + VAnchor = VAnchor.Absolute + }; + treeViewContainer.Height = 250; + editorPanel.AddChild(treeViewContainer); - if (scope.Count() > 1) + // add the tree view + var treeView = GetPartTreeView(selectedItem); + treeViewContainer.AddChild(treeView); + + // Add the selected item editor container. eventually we may want to make this a stretch container of some type + GuiWidget selectedItemContaner = new GuiWidget() { - editorPanel.AddChild(GetPartTreeView(rootSelection)); - } + HAnchor = HAnchor.Stretch, + VAnchor = VAnchor.Fit + }; + treeViewContainer.Height = 250; + editorPanel.AddChild(selectedItemContaner); - foreach (var scopeItem in scope) + treeView.AfterSelect += (s, e) => { - var selectedItem = scopeItem.item; - var selectedItemType = selectedItem.GetType(); + selectedItemContaner.CloseAllChildren(); + var selectedNodeItem = (IObject3D)treeView.SelectedNode.Tag; + var selectedNodeItemType = selectedNodeItem.GetType(); - var editorWidget = scopeItem.editor.Create(selectedItem, view3DWidget, theme); - editorWidget.HAnchor = HAnchor.Stretch; - editorWidget.VAnchor = VAnchor.Fit; - - if (scopeItem.item != rootSelection - && scopeItem.editor is PublicPropertyEditor) + HashSet mappedEditors = ApplicationController.Instance.GetEditorsForType(selectedNodeItemType); + if (ApplicationController.Instance.GetEditorsForType(selectedNodeItemType)?.FirstOrDefault() is IObject3DEditor editor) { + var editorWidget = editor.Create(selectedNodeItem, view3DWidget, theme); + editorWidget.HAnchor = HAnchor.Stretch; + editorWidget.VAnchor = VAnchor.Fit; + editorWidget.Padding = new BorderDouble(10, 10, 10, 0); // EditOutline section var sectionWidget = new SectionWidget( - scopeItem.displayName ?? "Unknown", + string.IsNullOrWhiteSpace(selectedNodeItem.Name) ? "Unknown" : selectedNodeItem.Name, editorWidget, theme); theme.ApplyBoxStyle(sectionWidget, margin: 0); editorWidget = sectionWidget; - } - else - { - editorWidget.Padding = 0; - } - editorPanel.AddChild(editorWidget); + selectedItemContaner.AddChild(editorWidget); - var buttons = new List(); + var buttons = new List(); - foreach (var nodeOperation in ApplicationController.Instance.Graph.Operations) - { - foreach (var type in nodeOperation.MappedTypes) + foreach (var nodeOperation in ApplicationController.Instance.Graph.Operations) { - if (type.IsAssignableFrom(selectedItemType) - && (nodeOperation.IsVisible == null || nodeOperation.IsVisible(selectedItem))) + foreach (var type in nodeOperation.MappedTypes) { - var button = new OperationButton(nodeOperation, selectedItem, theme) + if (type.IsAssignableFrom(selectedNodeItemType) + && (nodeOperation.IsVisible == null || nodeOperation.IsVisible(selectedNodeItem))) { - BackgroundColor = theme.MinimalShade, - Margin = theme.ButtonSpacing - }; - button.EnsureAvailablity(); - button.Click += (s, e) => - { - nodeOperation.Operation(selectedItem, scene).ConfigureAwait(false); - }; + var button = new OperationButton(nodeOperation, selectedNodeItem, theme) + { + BackgroundColor = theme.MinimalShade, + Margin = theme.ButtonSpacing + }; + button.EnsureAvailablity(); + button.Click += (s1, e1) => + { + nodeOperation.Operation(selectedNodeItem, scene).ConfigureAwait(false); + }; - buttons.Add(button); + buttons.Add(button); + } } } - } - if (buttons.Any()) - { - var toolbar = new Toolbar(theme) + if (buttons.Any()) { - HAnchor = HAnchor.Stretch, - VAnchor = VAnchor.Fit, - Padding = theme.ToolbarPadding, - Margin = new BorderDouble(0, 8) - }; - editorPanel.AddChild(toolbar); - - foreach (var button in buttons) - { - toolbar.AddChild(button); - } - - // TODO: Fix likely leak - selectedItem.Invalidated += (s, e) => - { - foreach (var button in toolbar.ActionArea.Children.OfType()) + var toolbar = new Toolbar(theme) { - button.EnsureAvailablity(); + HAnchor = HAnchor.Stretch, + VAnchor = VAnchor.Fit, + Padding = theme.ToolbarPadding, + Margin = new BorderDouble(0, 8) + }; + selectedItemContaner.AddChild(toolbar); + + foreach (var button in buttons) + { + toolbar.AddChild(button); } - }; + + // TODO: Fix likely leak + selectedNodeItem.Invalidated += (s2, e2) => + { + foreach (var button in toolbar.ActionArea.Children.OfType()) + { + button.EnsureAvailablity(); + } + }; + } + else + { + // If the button toolbar isn't added, ensure panel has bottom margin + editorWidget.Margin = editorWidget.Margin.Clone(bottom: 15); + } } - else - { - // If the button toolbar isn't added, ensure panel has bottom margin - editorWidget.Margin = editorWidget.Margin.Clone(bottom: 15); - } - } + }; + + treeView.SelectedNode = treeView.TopNode; } private void AddTree(IObject3D item, TreeNode parent) @@ -464,7 +457,7 @@ namespace MatterHackers.MatterControl.PartPreviewWindow return nameToWrite; } - private GuiWidget GetPartTreeView(IObject3D rootSelection) + private TreeView GetPartTreeView(IObject3D rootSelection) { var topNode = new TopNode() { @@ -474,42 +467,33 @@ namespace MatterHackers.MatterControl.PartPreviewWindow PointSize = theme.DefaultFontSize }; - // eventually we may want to make this a stretch container of some type - var treeViewContainer = new GuiWidget(); - var treeView = new TreeView(topNode) { TextColor = theme.Colors.PrimaryTextColor, PointSize = theme.DefaultFontSize }; - treeViewContainer.AddChild(treeView); - treeView.SuspendLayout(); selectionTreeNodes.Clear(); selectionTreeNodes.Add(rootSelection, topNode); // add the children to the root node - foreach (var child in item.Children) + foreach (var child in rootSelectedItem.Children) { AddTree(child, topNode); } treeView.ResumeLayout(); - var partTreeContainer = new SectionWidget("Part Details".Localize(), treeViewContainer, theme, serializationKey: UserSettingsKey.EditorPanelPartTree, defaultExpansion: true); - treeViewContainer.VAnchor = VAnchor.Absolute; - treeViewContainer.Height = 250; - - return partTreeContainer; + return treeView; } public void Save(ILibraryItem item, IObject3D content) { - this.item.Parent.Children.Modify(children => + this.rootSelectedItem.Parent.Children.Modify(children => { - children.Remove(this.item); + children.Remove(this.rootSelectedItem); children.Add(content); }); } diff --git a/SettingsManagement/UserSettings.cs b/SettingsManagement/UserSettings.cs index 684ecee39..f46a4e9b1 100644 --- a/SettingsManagement/UserSettings.cs +++ b/SettingsManagement/UserSettings.cs @@ -22,7 +22,6 @@ namespace MatterHackers.MatterControl public const string CredentialsInvalidReason = nameof(CredentialsInvalidReason); public const string defaultRenderSetting = nameof(defaultRenderSetting); public const string DisplayedTip_LoadFilament = nameof(DisplayedTip_LoadFilament); - public const string EditorPanelPartTree = nameof(EditorPanelPartTree); public const string EditorPanelExpanded = nameof(EditorPanelExpanded); public const string GCodeLineColorStyle = nameof(GCodeLineColorStyle); public const string GcodeModelView = nameof(GcodeModelView); diff --git a/Submodules/agg-sharp b/Submodules/agg-sharp index 374d12294..cc7fbfc1b 160000 --- a/Submodules/agg-sharp +++ b/Submodules/agg-sharp @@ -1 +1 @@ -Subproject commit 374d122946eefe057d7a6c72b16c93a3f76c81bb +Subproject commit cc7fbfc1baf618ef49920b73c180595a727f069d