namespace UIWidgets.Examples { using System; using System.Collections.Generic; using System.Linq; using UIWidgets; using UnityEngine; using UnityEngine.Serialization; /// /// Test TreeView. /// public class TestTreeView : MonoBehaviour { /// /// TreeView. /// [SerializeField] public TreeView Tree; /// /// Nodes. /// protected ObservableList> Nodes; /// /// UINodes. /// protected ObservableList> UINodes; /// /// Default nodes. /// protected Dictionary>> DefaultNodes; /// /// Initializes a new instance of the class. /// public TestTreeView() { var config = new List() { 10, 4, 5, 5, }; Nodes = GenerateTreeNodes(config, isExpanded: false); } /// /// Process selected nodes. /// public void ProcessSelectedNodes() { foreach (var node in Tree.SelectedNodes) { // do something with selected node Debug.Log(node.Item.Name); var component = Tree.GetItemComponent(node.Index); // not visible component will be null if (component != null) { // do something with component // component.ToggleEye(); Debug.Log(component); } } } /// /// Start this instance. /// protected virtual void Start() { Tree.Init(); Tree.Nodes = Nodes; // Tree.OnSelect.AddListener(ProcessSelectedNode); // SetComparison(); } /// /// Handle selected node event. /// Deselect node if match some condition. /// /// Node. protected virtual void ProcessSelectedNode(TreeNode node) { if (node.TotalNodesCount > 1) { Tree.Deselect(node.Index); } } /// /// Log selected node. /// public void TestSelect() { Debug.Log(Tree.SelectedIndex.ToString()); Debug.Log(Tree.SelectedItem.Node.Item.Name); } /// /// Add listener. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "HAA0603:Delegate allocation from a method group", Justification = "Required")] public void TestSelectAll() { Tree.NodeDeselected.AddListener(OnNodeDeselected); } /// /// Scroll to node. /// public void ScrollToNode() { var target = Tree.Nodes[9].Nodes[2].Nodes[1].Nodes[3]; Tree.ScrollToAnimated(target); Tree.Select(target); } /// /// Is now handle selected node? /// protected bool handleSelected; /// /// Handle node deselected event. /// /// Node. protected void OnNodeDeselected(TreeNode node) { if (handleSelected) { return; } handleSelected = true; Tree.DeselectNodeWithSubnodes(node); handleSelected = false; } /// /// Handle node selected event. /// /// Node. protected void OnNodeSelected(TreeNode node) { if (handleSelected) { return; } handleSelected = true; Tree.SelectNodeWithSubnodes(node); handleSelected = false; } /// /// Select fist node with sub-nodes and measure how much time it's takes. /// public void SelectFirstNodeWithSubnodes() { Tree.SelectNodeWithSubnodes(Nodes[0]); } /// /// Select first sub-node from second node. /// public void SelectNode() { Tree.Select(Nodes[1].Nodes[0]); } /// /// Select node with sub-nodes. /// public void SelectNodeWithSubnodes() { Tree.SelectNodeWithSubnodes(Nodes[1].Nodes[1]); } /// /// Select node and scroll to it. /// public void TestSelectAndScrollTo() { SelectAndScrollTo(Nodes[2].Nodes[1].Nodes[1]); } /// /// Select node and scroll to it. /// /// Node. public void SelectAndScrollTo(TreeNode node) { // expand parent nodes Tree.ExpandParentNodes(node); // select node Tree.Select(node); // scroll to node immediately // Tree.ScrollTo(Tree.SelectedIndex); // scroll to node animated Tree.ScrollToAnimated(Tree.SelectedIndex); } /// /// Use duplicates. /// public void UseDuplicates() { Nodes.OnChange += UpdateUINodes; UpdateUINodes(); } /// /// Process the destroy event. /// protected virtual void OnDestroy() { if (Nodes != null) { Nodes.OnChange -= UpdateUINodes; } } void UpdateUINodes() { UINodes = DuplicateNodes(Nodes); Tree.Nodes = UINodes; } ObservableList> DuplicateNodes(ObservableList> source) { var result = new ObservableList>(); foreach (var node in source) { result.Add(new TreeNode( node.Item, (node.Nodes == null) ? null : DuplicateNodes(node.Nodes), node.IsExpanded, node.IsVisible)); } return result; } /// /// Set node sort. /// public void SetComparison() { Nodes[0].Nodes.Comparison = comparisonDesc; Nodes.Comparison = comparisonDesc; } /// /// Test find node. /// /// Node id. [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "HAA0301:Closure Allocation Source", Justification = "Required")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "HAA0302:Display class allocation to capture closure", Justification = "Required")] public void TestFindNode(int id) { var node = Tree.FindNode(x => x.Item.Value == id); Debug.Log((node != null) ? node.Item.Name : "Node not found"); } readonly Dictionary> cache = new Dictionary>(); void Nodes2Cache(ObservableList> nodes) { if (nodes == null) { return; } foreach (var node in nodes) { cache[node.Item.Value] = node; Nodes2Cache(node.Nodes); } } /// /// Create cache. /// public void CreateCache() { cache.Clear(); Nodes2Cache(Tree.Nodes); } /// /// Get node from cache by id. /// /// Node id. public void GetFromCache(int id) { var node = cache[id]; Debug.Log((node != null) ? node.Item.Name : "Node not found"); } /// /// Add subnodes. /// public void AddSubNodes() { if (Nodes.Count == 0) { return; } // get parent node var node = Nodes[0]; // or find parent node by name /* var node = nodes.Find(x => x.Item.Name = "Node 2");*/ if (node.Nodes == null) { node.Nodes = new ObservableList>(); } var new_item1 = new TreeViewItem("Subnode 1"); var new_node1 = new TreeNode(new_item1); var new_item2 = new TreeViewItem("Subnode 2"); var new_node2 = new TreeNode(new_item2); var new_item3 = new TreeViewItem("Subnode 3"); var new_node3 = new TreeNode(new_item3); node.Nodes.BeginUpdate(); node.Nodes.Add(new_node1); node.Nodes.Add(new_node2); node.Nodes.Add(new_node3); node.Nodes.EndUpdate(); } /// /// Add node and scroll to it. /// public void ScrollToNewNode() { var new_item = new TreeViewItem("New node"); var new_node = new TreeNode(new_item); // nodes[0].Nodes.Add(new_node); Nodes.Add(new_node); // nodes.Insert(0, new_node); // ScrollToNode(new_node); ScrollToNodeAnimated(new_node); } /// /// Scroll to specified node. /// /// Node. public void ScrollToNode(TreeNode node) { // find index of node, DataSource contains list of visible nodes var index = Tree.Node2Index(node); // if node exists and visible if (index != -1) { // scroll to node Tree.ScrollTo(index); } } /// /// Scroll to node animated. /// /// Node. public void ScrollToNodeAnimated(TreeNode node) { // expand node, so DataSource contains list of visible nodes Tree.ExpandParentNodes(node); // get node index var index = Tree.Node2Index(node); // if node exists and visible if (index != -1) { // scroll to node Tree.ScrollToAnimated(index); } } /// /// Log selected nodes. /// public void GetSelectedNodes() { Debug.Log(Tree.SelectedIndex.ToString()); Debug.Log(UtilitiesCollections.List2String(Tree.SelectedIndices)); var selectedNodes = Tree.SelectedNodes; if (selectedNodes != null) { foreach (var node in selectedNodes) { Debug.Log(node.Item.Name); } } } /// /// Get node path. /// public void GetNodePath() { var path = Nodes[0].Nodes[0].Nodes[0].Path; foreach (var node in path) { Debug.Log(node.Item.Name); } } /// /// Select nodes. /// public void SelectNodes() { if ((Nodes.Count == 0) || (Nodes[0].Nodes.Count == 0)) { return; } // replace on find node "Node 1 - 1" var parent_node = Nodes[0].Nodes[0]; var children = new List>(); GetChildrenNodes(parent_node, children); // add children to selected nodes var selected_nodes = Tree.SelectedNodes; selected_nodes.AddRange(children); Tree.SelectedNodes = selected_nodes; // select only children // Tree.SelectedNodes = children; } /// /// Deselect nodes. /// public void DeselectNodes() { if ((Nodes.Count == 0) || (Nodes[0].Nodes.Count == 0)) { return; } // replace on find node "Node 1 - 1" var parent_node = Nodes[0].Nodes[0]; var children = new List>(); GetChildrenNodes(parent_node, children); // remove children from selected nodes var selected_nodes = Tree.SelectedNodes; foreach (var node in children) { selected_nodes.Remove(node); } Tree.SelectedNodes = selected_nodes; // deselect all // Tree.SelectedNodes = new List>(); } void GetChildrenNodes(TreeNode node, List> children) { if (node.Nodes == null) { return; } children.AddRange(node.Nodes); foreach (var n in node.Nodes) { GetChildrenNodes(n, children); } } /// /// Only one node can be selected at once. /// public void SetOnlyOnSelectable() { Tree.MultipleSelect = false; } /// /// Multiple nodes can be selected at once. /// public void SetMultipleSelectable() { Tree.MultipleSelect = true; } /// /// Compare nodes by Name in ascending order. /// protected Comparison> comparisonAsc = (x, y) => { return UtilitiesCompare.Compare(x.Item.LocalizedName ?? x.Item.Name, y.Item.LocalizedName ?? y.Item.Name); }; /// /// Compare nodes by Name in descending order. /// protected Comparison> comparisonDesc = (x, y) => { return -UtilitiesCompare.Compare(x.Item.LocalizedName ?? x.Item.Name, y.Item.LocalizedName ?? y.Item.Name); }; /// /// Sort in ascending order. /// public void SortAsc() { Nodes.BeginUpdate(); ApplyNodesSort(Nodes, comparisonAsc); Nodes.EndUpdate(); } /// /// Sort in descending order. /// public void SortDesc() { Nodes.BeginUpdate(); ApplyNodesSort(Nodes, comparisonDesc); Nodes.EndUpdate(); } /// /// Apply nodes sort. /// /// Node type. /// Nodes. /// Nodes comparison. public static void ApplyNodesSort(ObservableList> nodes, Comparison> comparison) { nodes.Sort(comparison); foreach (var node in nodes) { if (node.Nodes != null) { ApplyNodesSort(node.Nodes, comparison); } } } /// /// Remove node by name. /// /// Name. public void TestRemove(string name) { RemoveByName(Nodes, name); } /// /// Remove node by name. /// /// Nodes. /// Name. [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "HAA0301:Closure Allocation Source", Justification = "Required")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "HAA0302:Display class allocation to capture closure", Justification = "Required")] public static void RemoveByName(ObservableList> nodes, string name) { Remove(nodes, x => x.Item.Name == name); } /// /// Remove node if it's match specified function. /// /// Node type. /// Nodes. /// Match function. /// true if node removed; otherwise, false. public static bool Remove(ObservableList> nodes, Predicate> match) { var findedNode = nodes.Find(match); if (findedNode != null) { findedNode.Parent = null; // this.nodes.Add(findedNode as TreeNode); return true; } foreach (var node in nodes) { if (node.Nodes == null) { continue; } if (Remove(node.Nodes, match)) { return true; } } return false; } /// /// Test node position. /// /// Name. public void TestReorder(string name) { ChangePositionByName(Nodes, name, 0); } /// /// Change node position. /// /// Nodes. /// Node name. /// New position. [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "HAA0301:Closure Allocation Source", Justification = "Required")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "HAA0302:Display class allocation to capture closure", Justification = "Required")] public static void ChangePositionByName(ObservableList> nodes, string name, int position) { ChangePosition(nodes, x => x.Item.Name == name, position); } /// /// Change node position. /// /// Node type. /// Nodes. /// Node match. /// New position. /// true if position changed; otherwise, false. public static bool ChangePosition(ObservableList> nodes, Predicate> match, int position) { var findedNode = nodes.Find(match); if (findedNode != null) { nodes.BeginUpdate(); nodes.Remove(findedNode); nodes.Insert(position, findedNode); nodes.EndUpdate(); return true; } foreach (var node in nodes) { if (node.Nodes == null) { continue; } if (ChangePosition(node.Nodes, match, position)) { return true; } } return false; } /// /// Show tree with 10000 nodes. /// public void Test10K() { var config = new List() { 10, 10, 10, 10 }; Nodes = GenerateTreeNodes(config, isExpanded: true); Tree.Nodes = Nodes; } /// /// Icon for nodes. /// [SerializeField] [FormerlySerializedAs("icon")] protected Sprite Icon; /// /// Show tree with long nodes names. /// public void LongNames() { var config = new List() { 2, 2, 2, 2, 2, 2, 2, 2, 2 }; Nodes = GenerateTreeNodes(config, isExpanded: true, icon: Icon); Tree.Nodes = Nodes; } /// /// Set nodes by key. /// /// Key. public void PerformanceCheck(string k) { if (DefaultNodes == null) { DefaultNodes = new Dictionary>>(); var config1k = new List() { 10, 10, 10 }; DefaultNodes.Add("1k", GenerateTreeNodes(config1k, isExpanded: true)); var config5k = new List() { 5, 10, 10, 10 }; DefaultNodes.Add("5k", GenerateTreeNodes(config5k, isExpanded: true)); var config10k = new List() { 10, 10, 10, 10 }; DefaultNodes.Add("10k", GenerateTreeNodes(config10k, isExpanded: true)); var config50k = new List() { 5, 10, 10, 10, 10 }; DefaultNodes.Add("50k", GenerateTreeNodes(config50k, isExpanded: true)); } Nodes = DefaultNodes[k]; Tree.Nodes = DefaultNodes[k]; } /// /// Set tree nodes. /// public void SetTreeNodes() { Tree.Nodes = Nodes; Nodes.BeginUpdate(); var test_item = new TreeViewItem("added"); var test_node = new TreeNode(test_item); Nodes.Add(test_node); Nodes[1].IsVisible = false; Nodes[2].Nodes[1].IsVisible = false; Nodes.EndUpdate(); } /// /// Add node. /// public void AddNode() { var test_item = new TreeViewItem("New node"); var test_node = new TreeNode(test_item); Nodes.Add(test_node); } /// /// Toggle node. /// public void ToggleNode() { Nodes[0].Nodes[0].IsExpanded = !Nodes[0].Nodes[0].IsExpanded; } /// /// Expand all nodes. /// public void ExpandeAllNodes() { ToggleNodes(Nodes, true); } /// /// Change node names. /// public void ChangeNodesName() { Nodes[0].Item.Name = "Node renamed from code"; Nodes[0].Nodes[1].Item.Name = "Another node renamed from code"; } /// /// Reset filter. /// public void ResetFilter() { Nodes.BeginUpdate(); foreach (var node in Nodes) { SetVisible(node); } Nodes.EndUpdate(); } void SetVisible(TreeNode node) { if (node.Nodes != null) { foreach (var n in node.Nodes) { SetVisible(n); } } node.IsVisible = true; } /// /// Toggle nodes state. /// /// Nodes list. /// Expanded state. public void ToggleNodes(ObservableList> nodes, bool isExpanded) { if ((nodes == null) || (nodes.Count == 0)) { return; } nodes.BeginUpdate(); foreach (var node in nodes) { node.IsExpanded = isExpanded; ToggleNodes(node.Nodes, isExpanded); } nodes.EndUpdate(); } /// /// Filter nodes by name. /// /// Name. [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "HAA0301:Closure Allocation Source", Justification = "Required")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "HAA0302:Display class allocation to capture closure", Justification = "Required")] public void Filter(string nameContains) { Nodes.BeginUpdate(); SampleFilter(Nodes, x => UtilitiesCompare.Contains(x.Name, nameContains)); Nodes.EndUpdate(); } /// /// Clear nodes. /// public void Clear() { Nodes = new ObservableList>(); Tree.Nodes = Nodes; } /// /// Sample filter. /// Set node.IsVisible if it's match condition or it's subnodes match condition. /// /// Nodes. /// Match function. /// true if any node match condition; otherwise, false. protected static bool SampleFilter(ObservableList> nodes, Func filterFunc) { var result = false; foreach (var node in nodes) { var have_visible_children = (node.Nodes != null) && SampleFilter(node.Nodes, filterFunc); node.IsVisible = have_visible_children || filterFunc(node.Item); result |= node.IsVisible; } return result; } /// /// Generate nodes. /// /// Depth list. /// Start part of name. /// Is nodes expanded? /// Icon /// Start index in the depth list. /// Nodes. public static ObservableList> GenerateTreeNodes(List items, string nameStartsWith = "Node ", bool isExpanded = true, Sprite icon = null, int start = 0) { var count = items[start]; var result = new ObservableList>(true, count); result.BeginUpdate(); for (int i = 0; i < count; i++) { var item_name = string.Format("{0}{1}", nameStartsWith, (i + 1).ToString()); var item = new TreeViewItem(item_name, null); var nodes = items.Count > (start + 1) ? GenerateTreeNodes(items, string.Format("{0} - ", item_name), isExpanded, icon, start + 1) : null; result.Add(new TreeNode(item, nodes, isExpanded)); } result.EndUpdate(); return result; } /// /// Reload scene. /// public virtual void ReloadScene() { #if UNITY_5_3 || UNITY_5_3_OR_NEWER UnityEngine.SceneManagement.SceneManager.LoadScene(UnityEngine.SceneManagement.SceneManager.GetActiveScene().buildIndex); #else Application.LoadLevel(Application.loadedLevel); #endif } } }