// Searches for a navigable element starting from currentFocusable and scanning along the specified direction. // If no elements are found, wraps around from the other side of the panel and scan along the same direction // up to currentFocusable. If still no elements are found, returns currentFocusable. // // Though search order is hierarchy-based, the "best candidate" selection process is intended to be independent // from any hierarchy consideration. Scanned elements are validated using an intersection test between their // worldBound and a scanning validation rect, currentFocusable's worldBound extended to the panel's limits in // the direction of the search (or from the other side during the second "wrap-around" scan). // // The best candidate is the element whose border is "least advanced" in the scanning direction, using the // element's worldBound border opposite from scanning direction, left border when scanning right, right border // for scanning left, etc. See FocusableHierarchyTraversal for more details and how further ties are resolved. Focusable GetNextFocusable2D(Focusable currentFocusable, ChangeDirection direction) { if (!(currentFocusable is VisualElement ve)) { ve = m_Root; } Rect panelBounds = m_Root.worldBoundingBox; Rect panelRect = new Rect(panelBounds.position - Vector2.one, panelBounds.size + Vector2.one * 2); Rect rect = ve.worldBound; Rect validRect = new Rect(rect.position - Vector2.one, rect.size + Vector2.one * 2); if (direction == Up) { validRect.yMin = panelRect.yMin; } else if (direction == Down) { validRect.yMax = panelRect.yMax; } else if (direction == Left) { validRect.xMin = panelRect.xMin; } else if (direction == Right) { validRect.xMax = panelRect.xMax; } Focusable best = new FocusableHierarchyTraversal { currentFocusable = ve, direction = direction, validRect = validRect, firstPass = true }.GetBestOverall(m_Root); if (best != null) { return(best); } validRect = new Rect(rect.position - Vector2.one, rect.size + Vector2.one * 2); if (direction == Down) { validRect.yMin = panelRect.yMin; } else if (direction == Up) { validRect.yMax = panelRect.yMax; } else if (direction == Right) { validRect.xMin = panelRect.xMin; } else if (direction == Left) { validRect.xMax = panelRect.xMax; } best = new FocusableHierarchyTraversal { currentFocusable = ve, direction = direction, validRect = validRect, firstPass = false }.GetBestOverall(m_Root); if (best != null) { return(best); } return(currentFocusable); }
Focusable GetNextFocusable2D(Focusable currentFocusable, ChangeDirection direction) { if (!(currentFocusable is VisualElement ve)) { ve = m_Root; } ve = GetRootFocusable(ve); Rect rect = ve.worldBound; Rect validRect = new Rect(rect.position - Vector2.one, rect.size + Vector2.one * 2); if (direction == Up) { validRect.yMin = 0; } else if (direction == Down) { validRect.yMax = Screen.height; } else if (direction == Left) { validRect.xMin = 0; } else if (direction == Right) { validRect.xMax = Screen.width; } var best = new FocusableHierarchyTraversal { currentFocusable = ve, direction = direction, validRect = validRect, firstPass = true }.GetBestOverall(m_Root); if (best != null) { return(GetLeafFocusable(best)); } validRect = new Rect(rect.position - Vector2.one, rect.size + Vector2.one * 2); if (direction == Down) { validRect.yMin = 0; } else if (direction == Up) { validRect.yMax = Screen.height; } else if (direction == Right) { validRect.xMin = 0; } else if (direction == Left) { validRect.xMax = Screen.height; } best = new FocusableHierarchyTraversal { currentFocusable = ve, direction = direction, validRect = validRect, firstPass = false }.GetBestOverall(m_Root); if (best != null) { return(GetLeafFocusable(best)); } return(currentFocusable); }