From 359b1b26122c8bee4cf5871a4798b0af6a692478 Mon Sep 17 00:00:00 2001 From: Roman Shapiro Date: Fri, 3 Nov 2023 16:45:26 +0700 Subject: [PATCH] Fixes #428 --- samples/Myra.Samples.AllWidgets/AllWidgets.cs | 128 +++++- .../AllWidgets.cs | 127 +++++- .../Myra.Samples.GridContainer/GridGame.cs | 35 +- .../Graphics2D/UI/Containers/StackPanel.cs | 2 + .../Graphics2D/UI/Layouts/StackPanelLayout.cs | 5 + src/Myra/Graphics2D/UI/Misc/Tree.cs | 1 + src/Myra/Graphics2D/UI/Misc/TreeNode.cs | 1 + src/Myra/Graphics2D/UI/Misc/TreeView.cs | 412 ++++++++++++++++++ src/Myra/Graphics2D/UI/Misc/TreeViewNode.cs | 176 ++++++++ 9 files changed, 831 insertions(+), 56 deletions(-) create mode 100644 src/Myra/Graphics2D/UI/Misc/TreeView.cs create mode 100644 src/Myra/Graphics2D/UI/Misc/TreeViewNode.cs diff --git a/samples/Myra.Samples.AllWidgets/AllWidgets.cs b/samples/Myra.Samples.AllWidgets/AllWidgets.cs index 132f1004..ada95808 100644 --- a/samples/Myra.Samples.AllWidgets/AllWidgets.cs +++ b/samples/Myra.Samples.AllWidgets/AllWidgets.cs @@ -44,33 +44,115 @@ public AllWidgets() messageBox.ShowModal(Desktop); }; - var tree = new Tree - { - HasRoot = false - }; + var tree = new TreeView(); Grid.SetColumn(tree, 1); Grid.SetRow(tree, 12); Grid.SetColumnSpan(tree, 2); - var node1 = tree.AddSubNode("node1"); - var node2 = node1.AddSubNode("node2"); - var node3 = node2.AddSubNode("node3"); - node3.AddSubNode("node4"); - node3.AddSubNode("node5"); - node3.AddSubNode("node7"); - node3.AddSubNode("node8"); - node3.AddSubNode("node9"); - node3.AddSubNode("node10"); - - var node4 = node2.AddSubNode("node6"); - node4.AddSubNode("node11"); - node4.AddSubNode("node12"); - node4.AddSubNode("node13"); - node4.AddSubNode("node14"); - node4.AddSubNode("node15"); - node4.AddSubNode("node16"); - node4.AddSubNode("node17"); - node4.AddSubNode("node18"); + var node1 = tree.AddSubNode(new Label + { + Text = "node1" + }); + var node2 = node1.AddSubNode(new Label + { + Text = "node2" + }); + + var node4 = node2.AddSubNode(new Label + { + Text = "node6" + }); + node4.AddSubNode(new Label + { + Text = "node11" + }); + node4.AddSubNode(new Label + { + Text = "node12" + }); + node4.AddSubNode(new Label + { + Text = "node13" + }); + node4.AddSubNode(new Label + { + Text = "node14" + }); + node4.AddSubNode(new Label + { + Text = "node15" + }); + node4.AddSubNode(new Label + { + Text = "node16" + }); + node4.AddSubNode(new Label + { + Text = "node17" + }); + node4.AddSubNode(new Label + { + Text = "node18" + }); + + var node3 = node2.AddSubNode(new CheckButton + { + Content = new Label + { + Text = "CheckButton node" + } + }); + node3.AddSubNode(new Label + { + Text = "node4" + }); + node3.AddSubNode(new CheckButton + { + Content = new Label { Text = "CheckButton node2" }, + CheckPosition = CheckPosition.Right, + CheckContentSpacing = 8 + }); + + var imageButtonContent = new HorizontalStackPanel + { + Spacing = 4 + }; + + imageButtonContent.Widgets.Add(new Image + { + Renderable = DefaultAssets.UITextureRegionAtlas["icon-star"] + }); + + imageButtonContent.Widgets.Add(new Label + { + Text = "Button node" + }); + node3.AddSubNode(new Button + { + Content = imageButtonContent + }); + node3.AddSubNode(new HorizontalSlider()); + node3.AddSubNode(new SpinButton()); + + var imageButtonContent2 = new HorizontalStackPanel + { + Spacing = 4 + }; + + imageButtonContent2.Widgets.Add(new Label + { + Text = "ToggleButton node" + }); + imageButtonContent2.Widgets.Add(new Image + { + Renderable = DefaultAssets.UITextureRegionAtlas["icon-star"] + }); + + node3.AddSubNode(new ToggleButton + { + Content = imageButtonContent2 + }); + _gridRight.Widgets.Add(tree); } diff --git a/samples/Myra.Samples.CustomUIStylesheet/AllWidgets.cs b/samples/Myra.Samples.CustomUIStylesheet/AllWidgets.cs index f83d7501..d541ff50 100644 --- a/samples/Myra.Samples.CustomUIStylesheet/AllWidgets.cs +++ b/samples/Myra.Samples.CustomUIStylesheet/AllWidgets.cs @@ -17,32 +17,113 @@ public AllWidgets() debugWindow.ShowModal(Desktop); }; - var tree = new Tree - { - HasRoot = false, - }; + var tree = new TreeView(); Grid.SetColumn(tree, 1); Grid.SetRow(tree, 8); - var node1 = tree.AddSubNode("node1"); - var node2 = node1.AddSubNode("node2"); - var node3 = node2.AddSubNode("node3"); - node3.AddSubNode("node4"); - node3.AddSubNode("node5"); - node3.AddSubNode("node7"); - node3.AddSubNode("node8"); - node3.AddSubNode("node9"); - node3.AddSubNode("node10"); - - var node4 = node2.AddSubNode("node6"); - node4.AddSubNode("node11"); - node4.AddSubNode("node12"); - node4.AddSubNode("node13"); - node4.AddSubNode("node14"); - node4.AddSubNode("node15"); - node4.AddSubNode("node16"); - node4.AddSubNode("node17"); - node4.AddSubNode("node18"); + var node1 = tree.AddSubNode(new Label + { + Text = "node1" + }); + var node2 = node1.AddSubNode(new Label + { + Text = "node2" + }); + + var node4 = node2.AddSubNode(new Label + { + Text = "node6" + }); + node4.AddSubNode(new Label + { + Text = "node11" + }); + node4.AddSubNode(new Label + { + Text = "node12" + }); + node4.AddSubNode(new Label + { + Text = "node13" + }); + node4.AddSubNode(new Label + { + Text = "node14" + }); + node4.AddSubNode(new Label + { + Text = "node15" + }); + node4.AddSubNode(new Label + { + Text = "node16" + }); + node4.AddSubNode(new Label + { + Text = "node17" + }); + node4.AddSubNode(new Label + { + Text = "node18" + }); + + var node3 = node2.AddSubNode(new CheckButton + { + Content = new Label + { + Text = "CheckButton node" + } + }); + node3.AddSubNode(new Label + { + Text = "node4" + }); + node3.AddSubNode(new CheckButton + { + Content = new Label { Text = "CheckButton node2" }, + CheckPosition = CheckPosition.Right, + CheckContentSpacing = 8 + }); + + var imageButtonContent = new HorizontalStackPanel + { + Spacing = 4 + }; + + imageButtonContent.Widgets.Add(new Image + { + Renderable = DefaultAssets.UITextureRegionAtlas["icon-star"] + }); + + imageButtonContent.Widgets.Add(new Label + { + Text = "Button node" + }); + node3.AddSubNode(new Button + { + Content = imageButtonContent + }); + node3.AddSubNode(new HorizontalSlider()); + + var imageButtonContent2 = new HorizontalStackPanel + { + Spacing = 4 + }; + + imageButtonContent2.Widgets.Add(new Label + { + Text = "ToggleButton node" + }); + imageButtonContent2.Widgets.Add(new Image + { + Renderable = DefaultAssets.UITextureRegionAtlas["icon-star"] + }); + + node3.AddSubNode(new ToggleButton + { + Content = imageButtonContent2 + }); + _gridRight.Widgets.Add(tree); } } diff --git a/samples/Myra.Samples.GridContainer/GridGame.cs b/samples/Myra.Samples.GridContainer/GridGame.cs index 7b03d4b5..552fd79e 100644 --- a/samples/Myra.Samples.GridContainer/GridGame.cs +++ b/samples/Myra.Samples.GridContainer/GridGame.cs @@ -276,19 +276,34 @@ protected override void LoadContent() grid.Widgets.Add(vsliderValue); - var tree = new Tree - { - HasRoot = false, - }; + var tree = new TreeView(); Grid.SetColumn(tree, 3); Grid.SetRow(tree, 4); - var node1 = tree.AddSubNode("node1"); - var node2 = node1.AddSubNode("node2"); - var node3 = node2.AddSubNode("node3"); - node3.AddSubNode("node4"); - node3.AddSubNode("node5"); - node2.AddSubNode("node6"); + var node1 = tree.AddSubNode(new Label + { + Text = "node1" + }); + var node2 = node1.AddSubNode(new Label + { + Text = "node2" + }); + var node3 = node2.AddSubNode(new Label + { + Text = "node3" + }); + node3.AddSubNode(new Label + { + Text = "node4" + }); + node3.AddSubNode(new Label + { + Text = "node5" + }); + node2.AddSubNode(new Label + { + Text = "node6" + }); grid.Widgets.Add(tree); diff --git a/src/Myra/Graphics2D/UI/Containers/StackPanel.cs b/src/Myra/Graphics2D/UI/Containers/StackPanel.cs index b5816b7f..91bdae05 100644 --- a/src/Myra/Graphics2D/UI/Containers/StackPanel.cs +++ b/src/Myra/Graphics2D/UI/Containers/StackPanel.cs @@ -69,6 +69,8 @@ protected StackPanel() _proportions.CollectionChanged += (s, e) => InvalidateChildren(); } + public int GetCellSize(int index) => _layout.GetCellSize(index); + private void InvalidateChildren() { _childrenDirty = true; diff --git a/src/Myra/Graphics2D/UI/Layouts/StackPanelLayout.cs b/src/Myra/Graphics2D/UI/Layouts/StackPanelLayout.cs index 5bf6e213..1f03bbb9 100644 --- a/src/Myra/Graphics2D/UI/Layouts/StackPanelLayout.cs +++ b/src/Myra/Graphics2D/UI/Layouts/StackPanelLayout.cs @@ -98,5 +98,10 @@ public void Arrange(IEnumerable widgets, Rectangle bounds) UpdateWidgets(widgets); _layout.Arrange(widgets, bounds); } + + public int GetCellSize(int index) + { + return Orientation == Orientation.Horizontal ? _layout.GetColumnWidth(index) : _layout.GetRowHeight(index); + } } } \ No newline at end of file diff --git a/src/Myra/Graphics2D/UI/Misc/Tree.cs b/src/Myra/Graphics2D/UI/Misc/Tree.cs index b11dac65..37aa0d3c 100644 --- a/src/Myra/Graphics2D/UI/Misc/Tree.cs +++ b/src/Myra/Graphics2D/UI/Misc/Tree.cs @@ -16,6 +16,7 @@ namespace Myra.Graphics2D.UI { + [Obsolete("Use TreeView")] public class Tree : TreeNode { private readonly List _allNodes = new List(); diff --git a/src/Myra/Graphics2D/UI/Misc/TreeNode.cs b/src/Myra/Graphics2D/UI/Misc/TreeNode.cs index b2ee9846..fe3f8199 100644 --- a/src/Myra/Graphics2D/UI/Misc/TreeNode.cs +++ b/src/Myra/Graphics2D/UI/Misc/TreeNode.cs @@ -14,6 +14,7 @@ namespace Myra.Graphics2D.UI { + [Obsolete("Use TreeView")] public class TreeNode : Widget { private readonly SingleItemLayout _layout; diff --git a/src/Myra/Graphics2D/UI/Misc/TreeView.cs b/src/Myra/Graphics2D/UI/Misc/TreeView.cs new file mode 100644 index 00000000..28f72c3c --- /dev/null +++ b/src/Myra/Graphics2D/UI/Misc/TreeView.cs @@ -0,0 +1,412 @@ +using System; +using System.Collections.Generic; +using Myra.Graphics2D.UI.Styles; +using System.ComponentModel; + +#if MONOGAME || FNA +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Input; +#elif STRIDE +using Stride.Core.Mathematics; +using Stride.Input; +#else +using System.Drawing; +using Myra.Platform; +#endif + +namespace Myra.Graphics2D.UI +{ + public class TreeView : Widget + { + private readonly StackPanelLayout _layout = new StackPanelLayout(Orientation.Vertical); + private readonly List _allNodes = new List(); + private TreeViewNode _selectedRow; + private bool _rowInfosDirty = true; + + internal List AllNodes => _allNodes; + + private TreeViewNode HoverRow { get; set; } + + public TreeViewNode SelectedRow + { + get + { + return _selectedRow; + } + + set + { + if (value == _selectedRow) + { + return; + } + + _selectedRow = value; + + var ev = SelectionChanged; + if (ev != null) + { + ev(this, EventArgs.Empty); + } + } + } + + [Category("Appearance")] + public IBrush SelectionBackground { get; set; } + + [Category("Appearance")] + public IBrush SelectionHoverBackground { get; set; } + + public event EventHandler SelectionChanged; + + public TreeView(string styleName = Stylesheet.DefaultStyleName) + { + ChildrenLayout = _layout; + AcceptsKeyboardFocus = true; + HorizontalAlignment = HorizontalAlignment.Stretch; + VerticalAlignment = VerticalAlignment.Stretch; + SetStyle(styleName); + } + + public override void OnKeyDown(Keys k) + { + base.OnKeyDown(k); + + if (SelectedRow == null) + { + return; + } + + int index = 0; + IList parentWidgets = null; + if (SelectedRow.ParentNode != null) + { + parentWidgets = SelectedRow.ParentNode.ChildNodesGrid.Widgets; + index = parentWidgets.IndexOf(SelectedRow); + if (index == -1) + { + return; + } + } + + switch (k) + { + case Keys.Enter: + SelectedRow.IsExpanded = !SelectedRow.IsExpanded; + break; + case Keys.Up: + { + if (parentWidgets != null) + { + if (index == 0 && SelectedRow.ParentNode != null) + { + SelectedRow = SelectedRow.ParentNode; + } + else if (index > 0) + { + var previousRow = (TreeViewNode)parentWidgets[index - 1]; + if (!previousRow.IsExpanded || previousRow.ChildNodesCount == 0) + { + SelectedRow = previousRow; + } + else + { + SelectedRow = (TreeViewNode)previousRow.ChildNodesGrid.Widgets[previousRow.ChildNodesCount - 1]; + } + } + } + } + break; + case Keys.Down: + { + if (SelectedRow.IsExpanded && SelectedRow.ChildNodesCount > 0) + { + SelectedRow = (TreeViewNode)SelectedRow.ChildNodesGrid.Widgets[0]; + } + else if (parentWidgets != null && index + 1 < parentWidgets.Count) + { + SelectedRow = (TreeViewNode)parentWidgets[index + 1]; + } + else if (parentWidgets != null && index + 1 >= parentWidgets.Count) + { + var parentOfParent = SelectedRow.ParentNode.ParentNode; + if (parentOfParent != null) + { + var parentIndex = parentOfParent.ChildNodesGrid.Widgets.IndexOf(SelectedRow.ParentNode); + if (parentIndex + 1 < parentOfParent.ChildNodesCount) + { + SelectedRow = (TreeViewNode)parentOfParent.ChildNodesGrid.Widgets[parentIndex + 1]; + } + } + } + } + break; + } + } + + public override void OnTouchDown() + { + base.OnTouchDown(); + + if (Desktop == null) + { + return; + } + + SetHoverRow(Desktop.TouchPosition.Value); + + if (HoverRow != null && HoverRow.RowVisible) + { + SelectedRow = HoverRow; + } + } + + public override void OnTouchDoubleClick() + { + base.OnTouchDoubleClick(); + + if (HoverRow != null) + { + if (!HoverRow.RowVisible) + { + return; + } + + if (HoverRow.Mark.Visible && !HoverRow.Mark.IsTouchInside) + { + HoverRow.Mark.DoClick(); + } + } + } + + private Rectangle BuildRowRect(TreeViewNode rowInfo) + { + var rowPos = ToLocal(rowInfo.ToGlobal(rowInfo.ActualBounds.Location)); + + return new Rectangle(ActualBounds.Left, rowPos.Y, ActualBounds.Width, rowInfo.ContentHeight); + } + + private void SetHoverRow(Point position) + { + if (!ContainsGlobalPoint(position)) + { + return; + } + + position = ToLocal(position); + foreach (var rowInfo in _allNodes) + { + if (rowInfo.RowVisible) + { + var rect = BuildRowRect(rowInfo); + if (rect.Contains(position)) + { + HoverRow = rowInfo; + return; + } + } + } + } + + public override void OnMouseMoved() + { + base.OnMouseMoved(); + + HoverRow = null; + + if (Desktop == null) + { + return; + } + + SetHoverRow(Desktop.MousePosition); + } + + public override void OnMouseLeft() + { + base.OnMouseLeft(); + + HoverRow = null; + } + + public TreeViewNode AddSubNode(Widget content) + { + var result = new TreeViewNode(this, content, StyleName); + Grid.SetRow(result, Children.Count); + + Children.Add(result); + + return result; + } + + public void RemoveAllSubNodes() + { + _allNodes.Clear(); + _selectedRow = null; + HoverRow = null; + } + + private bool Iterate(TreeViewNode node, Func action) + { + if (!action(node)) + { + return false; + } + + foreach (var widget in node.ChildNodesGrid.ChildrenCopy) + { + var subNode = (TreeViewNode)widget; + if (!Iterate(subNode, action)) + { + return false; + } + } + + return true; + } + + /// + /// Iterates through all nodes + /// + /// Called for each node, returning false breaks iteration + public void Iterate(Func action) + { + foreach (TreeViewNode node in ChildrenCopy) + { + Iterate(node, action); + } + } + + private static void RecursiveUpdateRowVisibility(TreeViewNode tree) + { + tree.RowVisible = true; + + if (tree.IsExpanded) + { + foreach (var widget in tree.ChildNodesGrid.ChildrenCopy) + { + var TreeViewNode = (TreeViewNode)widget; + RecursiveUpdateRowVisibility(TreeViewNode); + } + } + } + + private void UpdateRowInfos() + { + foreach (var rowInfo in _allNodes) + { + rowInfo.RowVisible = false; + } + + foreach (TreeViewNode node in ChildrenCopy) + { + RecursiveUpdateRowVisibility(node); + } + } + + protected override void InternalArrange() + { + base.InternalArrange(); + _rowInfosDirty = true; + } + + public override void InternalRender(RenderContext context) + { + if (_rowInfosDirty) + { + UpdateRowInfos(); + _rowInfosDirty = false; + } + if (SelectionBackground != null) + { + if (HoverRow != null && HoverRow != SelectedRow && SelectionHoverBackground != null) + { + var rect = BuildRowRect(HoverRow); + SelectionHoverBackground.Draw(context, rect); + } + + if (SelectedRow != null && SelectedRow.RowVisible) + { + var rect = BuildRowRect(SelectedRow); + SelectionBackground.Draw(context, rect); + } + } else + { + if (HoverRow != null && SelectionHoverBackground != null) + { + var rect = BuildRowRect(HoverRow); + SelectionHoverBackground.Draw(context, rect); + } + } + + base.InternalRender(context); + } + + private static bool FindPath(Stack path, TreeViewNode node) + { + var top = path.Peek(); + + for (var i = 0; i < top.ChildNodesCount; ++i) + { + var child = top.GetSubNode(i); + + if (child == node) + { + return true; + } + + path.Push(child); + + if (FindPath(path, node)) + { + return true; + } + + path.Pop(); + } + + return false; + } + + + /// + /// Expands path to the node + /// + /// + public void ExpandPath(TreeViewNode node) + { + var path = new Stack(); + + foreach (TreeViewNode childNode in Children) + { + path.Push(childNode); + } + + if (!FindPath(path, node)) + { + // Path not found + return; + } + + while (path.Count > 0) + { + var p = path.Pop(); + p.IsExpanded = true; + } + } + + protected override void InternalSetStyle(Stylesheet stylesheet, string name) + { + base.InternalSetStyle(stylesheet, name); + ApplyTreeViewStyle(stylesheet.TreeStyles.SafelyGetStyle(name)); + } + + public void ApplyTreeViewStyle(TreeStyle style) + { + ApplyWidgetStyle(style); + + SelectionBackground = style.SelectionBackground; + SelectionHoverBackground = style.SelectionHoverBackground; + } + } +} \ No newline at end of file diff --git a/src/Myra/Graphics2D/UI/Misc/TreeViewNode.cs b/src/Myra/Graphics2D/UI/Misc/TreeViewNode.cs new file mode 100644 index 00000000..5394b17a --- /dev/null +++ b/src/Myra/Graphics2D/UI/Misc/TreeViewNode.cs @@ -0,0 +1,176 @@ +using System; +using Myra.Graphics2D.UI.Styles; + +#if MONOGAME || FNA +using Microsoft.Xna.Framework; +#elif STRIDE +using Stride.Core.Mathematics; +#else +using Color = FontStashSharp.FSColor; +#endif + +namespace Myra.Graphics2D.UI +{ + public class TreeViewNode : Widget + { + private readonly GridLayout _layout = new GridLayout(); + private readonly TreeView _topTree; + private readonly VerticalStackPanel _childNodesStackPanel; + private readonly ToggleButton _mark; + private readonly Widget _content; + + public bool IsExpanded + { + get { return _mark.IsPressed; } + + set { _mark.IsPressed = value; } + } + + public ToggleButton Mark => _mark; + + internal VerticalStackPanel ChildNodesGrid => _childNodesStackPanel; + + public int ContentHeight => _layout.GetRowHeight(0); + + public int ChildNodesCount => _childNodesStackPanel.Children.Count; + + internal bool RowVisible { get; set; } + + public TreeViewNode ParentNode { get; internal set; } + + + internal TreeViewNode(TreeView topTree, Widget content, string styleName = Stylesheet.DefaultStyleName) + { + _layout.ColumnSpacing = 2; + _layout.RowSpacing = 2; + ChildrenLayout = _layout; + + + _topTree = topTree; + + if (_topTree != null) + { + _topTree.AllNodes.Add(this); + } + + _mark = new ToggleButton(null) + { + HorizontalAlignment = HorizontalAlignment.Left, + VerticalAlignment = VerticalAlignment.Center, + Content = new Image() + }; + + _mark.PressedChanged += (s, a) => + { + _childNodesStackPanel.Visible = _mark.IsPressed; + }; + + Children.Add(_mark); + + _content = content; + if (_content != null) + { + Grid.SetColumn(_content, 1); + Children.Add(_content); + } + + HorizontalAlignment = HorizontalAlignment.Stretch; + VerticalAlignment = VerticalAlignment.Stretch; + + _layout.ColumnsProportions.Add(new Proportion(ProportionType.Auto)); + _layout.ColumnsProportions.Add(new Proportion(ProportionType.Fill)); + + _layout.RowsProportions.Add(new Proportion(ProportionType.Auto)); + _layout.RowsProportions.Add(new Proportion(ProportionType.Auto)); + + // Second is yet another grid holding child nodes + _childNodesStackPanel = new VerticalStackPanel + { + Visible = false, + }; + Grid.SetColumn(_childNodesStackPanel, 1); + Grid.SetRow(_childNodesStackPanel, 1); + + Children.Add(_childNodesStackPanel); + + SetStyle(styleName); + + UpdateMark(); + } + + private void MarkOnUp(object sender, EventArgs args) + { + _childNodesStackPanel.Visible = false; + } + + protected virtual void UpdateMark() + { + _mark.Visible = _childNodesStackPanel.Children.Count > 0; + } + + public virtual void RemoveAllSubNodes() + { + _childNodesStackPanel.Children.Clear(); + UpdateMark(); + } + + public TreeViewNode AddSubNode(Widget content) + { + var result = new TreeViewNode(_topTree, content, StyleName) + { + ParentNode = this + }; + Grid.SetRow(result, _childNodesStackPanel.Children.Count); + + _childNodesStackPanel.Children.Add(result); + + UpdateMark(); + + return result; + } + + public TreeViewNode GetSubNode(int index) + { + return (TreeViewNode)_childNodesStackPanel.Children[index]; + } + + public void RemoveSubNode(TreeViewNode subNode) + { + _childNodesStackPanel.Children.Remove(subNode); + if (_topTree != null && _topTree.SelectedRow == subNode) + { + _topTree.SelectedRow = null; + } + } + + public void RemoveSubNodeAt(int index) + { + var subNode = _childNodesStackPanel.Children[index]; + _childNodesStackPanel.Children.RemoveAt(index); + if (_topTree.SelectedRow == subNode) + { + _topTree.SelectedRow = null; + } + } + + public void ApplyTreeViewNodeStyle(TreeStyle style) + { + ApplyWidgetStyle(style); + + if (style.MarkStyle != null) + { + _mark.ApplyButtonStyle(style.MarkStyle); + if (style.MarkStyle.ImageStyle != null) + { + var image = (Image)_mark.Content; + image.ApplyPressableImageStyle(style.MarkStyle.ImageStyle); + } + } + } + + protected override void InternalSetStyle(Stylesheet stylesheet, string name) + { + ApplyTreeViewNodeStyle(stylesheet.TreeStyles.SafelyGetStyle(name)); + } + } +} \ No newline at end of file