#if ZLINQ_UNITY_UIELEMENTS_SUPPORT #nullable enable using System.Runtime.InteropServices; using UnityEngine.UIElements; using ZLinq.Traversables; namespace ZLinq { public static class VisualTraverserExtensions { public static VisualElementTraverser AsTraverser(this VisualElement origin) => new(origin); // type inference helper public static ValueEnumerable, VisualElement> Children(this VisualElementTraverser traverser) => traverser.Children(); public static ValueEnumerable, VisualElement> ChildrenAndSelf(this VisualElementTraverser traverser) => traverser.ChildrenAndSelf(); public static ValueEnumerable, VisualElement> Descendants(this VisualElementTraverser traverser) => traverser.Descendants(); public static ValueEnumerable, VisualElement> DescendantsAndSelf(this VisualElementTraverser traverser) => traverser.DescendantsAndSelf(); public static ValueEnumerable, VisualElement> Ancestors(this VisualElementTraverser traverser) => traverser.Ancestors(); public static ValueEnumerable, VisualElement> AncestorsAndSelf(this VisualElementTraverser traverser) => traverser.AncestorsAndSelf(); public static ValueEnumerable, VisualElement> BeforeSelf(this VisualElementTraverser traverser) => traverser.BeforeSelf(); public static ValueEnumerable, VisualElement> BeforeSelfAndSelf(this VisualElementTraverser traverser) => traverser.BeforeSelfAndSelf(); public static ValueEnumerable, VisualElement> AfterSelf(this VisualElementTraverser traverser) => traverser.AfterSelf(); public static ValueEnumerable, VisualElement> AfterSelfAndSelf(this VisualElementTraverser traverser) => traverser.AfterSelfAndSelf(); // direct shortcut public static ValueEnumerable, VisualElement> Children(this VisualElement origin) => origin.AsTraverser().Children(); public static ValueEnumerable, VisualElement> ChildrenAndSelf(this VisualElement origin) => origin.AsTraverser().ChildrenAndSelf(); public static ValueEnumerable, VisualElement> Descendants(this VisualElement origin) => origin.AsTraverser().Descendants(); public static ValueEnumerable, VisualElement> DescendantsAndSelf(this VisualElement origin) => origin.AsTraverser().DescendantsAndSelf(); public static ValueEnumerable, VisualElement> Ancestors(this VisualElement origin) => origin.AsTraverser().Ancestors(); public static ValueEnumerable, VisualElement> AncestorsAndSelf(this VisualElement origin) => origin.AsTraverser().AncestorsAndSelf(); public static ValueEnumerable, VisualElement> BeforeSelf(this VisualElement origin) => origin.AsTraverser().BeforeSelf(); public static ValueEnumerable, VisualElement> BeforeSelfAndSelf(this VisualElement origin) => origin.AsTraverser().BeforeSelfAndSelf(); public static ValueEnumerable, VisualElement> AfterSelf(this VisualElement origin) => origin.AsTraverser().AfterSelf(); public static ValueEnumerable, VisualElement> AfterSelfAndSelf(this VisualElement origin) => origin.AsTraverser().AfterSelfAndSelf(); } [StructLayout(LayoutKind.Auto)] public struct VisualElementTraverser : ITraverser { static readonly object CalledTryGetNextChild = new object(); static readonly object ParentNotFound = new object(); readonly VisualElement visualElement; object? initializedState; // CalledTryGetNext or Parent(for sibling operations) int childCount; // self childCount(TryGetNextChild) or parent childCount(TryGetSibling) int index; public VisualElementTraverser(VisualElement origin) { this.visualElement = origin; this.initializedState = null; this.childCount = 0; this.index = 0; } public VisualElement Origin => visualElement; public VisualElementTraverser ConvertToTraverser(VisualElement next) => new(next); public bool TryGetParent(out VisualElement parent) { var veParent = visualElement.parent; if (veParent != null) { parent = veParent; return true; } parent = default!; return false; } public bool TryGetChildCount(out int count) { count = visualElement.childCount; return true; } public bool TryGetHasChild(out bool hasChild) { hasChild = visualElement.childCount != 0; return true; } public bool TryGetNextChild(out VisualElement child) { if (initializedState == null) { initializedState = CalledTryGetNextChild; childCount = visualElement.childCount; } if (index < childCount) { child = visualElement[index++]; return true; } child = default!; return false; } public bool TryGetNextSibling(out VisualElement next) { if (initializedState == null) { var veParent = visualElement.parent; if (veParent == null) { initializedState = ParentNotFound; next = default!; return false; } // cache parent and childCount initializedState = veParent; childCount = veParent.childCount; // parent's childCount index = veParent.IndexOf(visualElement) + 1; } else if (initializedState == ParentNotFound) { next = default!; return false; } var parent = (VisualElement)initializedState; if (index < childCount) { next = parent[index++]; return true; } next = default!; return false; } public bool TryGetPreviousSibling(out VisualElement previous) { if (initializedState == null) { var veParent = visualElement.parent; if (veParent == null) { initializedState = ParentNotFound; previous = default!; return false; } initializedState = veParent; childCount = veParent.IndexOf(visualElement); // not childCount but means `to` index = 0; // 0 to siblingIndex } else if (initializedState == ParentNotFound) { previous = default!; return false; } var parent = (VisualElement)initializedState; if (index < childCount) { previous = parent[index++]; return true; } previous = default!; return false; } public void Dispose() { } } } #endif