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