Skip to content

Instantly share code, notes, and snippets.

@andrew-raphael-lukasik
Last active August 9, 2023 19:13
Show Gist options
  • Save andrew-raphael-lukasik/d38a11bf0559a723617b9deaeedb2eac to your computer and use it in GitHub Desktop.
Save andrew-raphael-lukasik/d38a11bf0559a723617b9deaeedb2eac to your computer and use it in GitHub Desktop.
UIToolkit, QoL extension methods that should be built-in
// src* = https://gist.github.com/andrew-raphael-lukasik/d38a11bf0559a723617b9deaeedb2eac
using UnityEngine;
using UnityEngine.UIElements;
public static class ExtensionMethods_UIToolkit
{
public static bool Q<T>(this VisualElement root, string name, out T result, params string[] classes)
where T : VisualElement
{
result = root.Find<T>(name, classes);
bool found = result != null;
if( !found ) Debug.LogWarning($"no <{typeof(T).Name}> name:{name} classes:{JsonUtility.ToJson(classes)} found!");
return found;
}
public static bool Q<T>(this VisualElement root, out T result, params string[] classes)
where T : VisualElement
{
result = root.Find<T>(null, classes);
bool found = result != null;
if( !found ) Debug.LogWarning($"no <{typeof(T).Name}> classes:{JsonUtility.ToJson(classes)} found!");
return found;
}
public static T For<T>(this VisualElement root, string name, System.Action<T> action, params string[] classes)
where T : VisualElement
{
T result = root.Find<T>(name, classes);
if (result != null)
{
if (action != null)
action(result);
int numFound = root.Query<T>(name, classes).ToList().Count;
if (numFound == 0) Debug.LogWarning($"no <{typeof(T).Name}> name:{name} classes:{JsonUtility.ToJson(classes)} found!");
else if (numFound != 1) Debug.LogWarning($"number of <{typeof(T).Name}> name:{name} classes:{JsonUtility.ToJson(classes)} found is {numFound} where only 1 was expected");
}
return result;
}
public static T Find<T>(this VisualElement root, string name, params string[] classes)
where T : VisualElement
{
T result = default(T);
if (root != null)
{
result = UQueryExtensions.Q<T>(root, name, classes);
if (result == null) Debug.LogWarning($"{root.name}.'{name}'<{typeof(T).Name}> classes:{JsonUtility.ToJson(classes)} not found");
}
else Debug.LogWarning($"given {nameof(root)} is null!");
return result;
}
public static T Find<T>(this VisualElement root, params string[] classes)
where T : VisualElement
=> Find<T>(root, null, classes);
public static void Foreach<T>(this VisualElement root, string name, System.Action<T> action, params string[] classes)
where T : VisualElement
{
var query = root.Query<T>(name, classes);
int numFound = query.ToList().Count;
if (numFound == 0) Debug.LogWarning($"no <{typeof(T).Name}> name:{name} classes:{JsonUtility.ToJson(classes)} found!");
else if (numFound != 1) Debug.LogWarning($"number of <{typeof(T).Name}> name:{name} classes:{JsonUtility.ToJson(classes)} found is {numFound} where only 1 was expected");
query.ForEach(action);
}
public static void Foreach<T>(this VisualElement root, System.Action<T> action, params string[] classes)
where T : VisualElement
=> Foreach<T>(root, action, classes);
}
@andrew-raphael-lukasik
Copy link
Author

andrew-raphael-lukasik commented Oct 29, 2021

if( ROOT.Q<Button>( "button-name" , out var button ) )
	button.clicked += () => Debug.Log("button clicked");

Main utility here is that when this Q fails it won't throw exceptions (breaking everything) and will log you an informative message about it so you don't need to write a code to handle that detail everywhere.

@MostHated
Copy link

Thanks for sharing this. It's a great idea and fits nicely into all my current usages and they just start taking advantage of it right away. 👍

@andrew-raphael-lukasik
Copy link
Author

Hi, happy to hear it's useful!

I feel obliged to note that one cool thing I totally missed while writing this piece was a ForEach query present in the API. It's very easy to miss if you ask me:

ROOT.Query<Button>( "button-name" ).ForEach( (button) =>
{
	button.clicked += () => Debug.Log("button clicked");
} );

It executes for each matching element (if found any), which is irreplaceable in some cases.

@MostHated
Copy link

Yes, I am very much a fan of that one. I use it quite often when setting up animations on a large number of elements and things of that nature. Or, at least a "close enough" approach.

https://github.com/instance-id/ElementAnimationToolkit/blob/9174582bad733f90626810055dca2f149e883477/Assets/instance.id/ElementAnimationToolkit/Runtime/Scripts/UI/EATKEditor.cs#L887

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment