Skip to content

Instantly share code, notes, and snippets.

@RyanGarber
Last active August 2, 2023 06:50
Show Gist options
  • Save RyanGarber/16856079f4e7f73d23df0be29489aec7 to your computer and use it in GitHub Desktop.
Save RyanGarber/16856079f4e7f73d23df0be29489aec7 to your computer and use it in GitHub Desktop.
jQuery-style queries for Unity's UI Toolkit
using System.Linq;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.UIElements;
public class UIBehaviour : MonoBehaviour
{
public List<VisualElement> Find(string query, bool one = false)
{
string selectorRegex = @"([\*#\.A-Za-z0-9-_]+)";
static char Trim(string selector) => selector.Contains('>') ? '>' : (selector.Contains(',') ? ',' : ' ');
List<VisualElement> elements = new() { GetComponent<UIDocument>().rootVisualElement };
MatchCollection matches = Regex.Matches(query, selectorRegex);
string Selector(int i) => query[matches[i].Index..(matches[i].Index + matches[i].Length)];
char Relation(int i) => i > 0 ? Trim(query[(matches[i - 1].Index + matches[i - 1].Length)..matches[i].Index]) : ' ';
for (int current = 0; current < matches.Count; current++)
{
List<string> selectors = new() { Selector(current) };
char relation = Relation(current);
if (current < matches.Count - 1)
{
for (int next = current + 1; next < matches.Count; next++)
{
if (Relation(next) != ',') break;
selectors.Add(Selector(next));
current++;
}
}
elements = FindChildren(elements, selectors, relation != '>', one && current == matches.Count - 1);
}
return elements;
}
public static List<VisualElement> FindChildren(IEnumerable<VisualElement> elements, List<string> selectors, bool deep, bool one)
{
List<VisualElement> results = new();
foreach (VisualElement child in elements)
{
if (child == null) continue;
if (Match(child, selectors))
{
results.Add(child);
if (one) return results;
}
if (deep)
{
results.AddRange(FindChildren(child.Children(), selectors, deep, one));
if (one && results.Count > 0) return results;
}
}
return results;
}
public static bool Match(VisualElement element, List<string> selectors)
{
foreach (string selector in selectors)
{
if (selector == "*") return true;
if (element.GetType().Name == selector) return true;
if (selector.StartsWith('#') && element.name == selector[1..]) return true;
if (selector.StartsWith('.') && element.ClassListContains(selector[1..])) return true;
}
return false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment