Skip to content

Rosalina is a code generation tool for Unity's UI documents. It generates C# code-behind script based on a UXML template.

License

MIT, Unknown licenses found

Licenses found

MIT
LICENSE
Unknown
LICENSE.meta
Notifications You must be signed in to change notification settings

Eastrall/Rosalina

Repository files navigation

Rosalina

openupm

Rosalina is a code generation tool for Unity's UI documents make with the new UI Toolkit. It automatically generates C# UI binding scripts based on a UXML document template.

Key features

  • UI Documents
    • Automatic C# bindings generation
    • C# document script
  • EditorWindow support
    • Automatic C# bindings generation
    • C# document script
  • Custom Components
    • Automatic C# bindings generation
    • C# document script

How to install

Rosalina can either be installed via OpenUPM: https://openupm.com/packages/com.eastylabs.rosalina/ Or by using the following git repository: https://github.com/Eastrall/Rosalina.git

Installing via OpenUPM

Rosalina is now available via OpenUPM: https://openupm.com/packages/com.eastylabs.rosalina/

OpenUPM provides a detailed explanation of how to add packages to unity.

Installing via git repository

In Unity, navigate to Window -> Package Manager:

image

In the Package Manager, click on the + on the top left and select Add package from git URL...

image

No use the following path to install Rosalina https://github.com/Eastrall/Rosalina.git

image

You can now start to work with Rosalina.

How it works

Rosalina watches your changes related to all *.uxml files contained in the Assets folder of your project, parses its content and generates the according C# UI binding code based on the element's names.

Take for instance the following UXML template:

SampleDocument.uxml

<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements"
         xsi="http://www.w3.org/2001/XMLSchema-instance"
         engine="UnityEngine.UIElements" editor="UnityEditor.UIElements"
         noNamespaceSchemaLocation="../../UIElementsSchema/UIElements.xsd"
         editor-extension-mode="False">
    <ui:VisualElement>
        <ui:Label text="Label" name="TitleLabel"/>
        <ui:Button text="Button" name="Button"/>
    </ui:VisualElement>
</ui:UXML>

Rosalina's AssetProcessor will automatically genearte the following C# UI bindings script:

ℹ️ Note: All generated files are located in the Assets/Rosalina/AutoGenerated/ folder.

SampleDocument.g.cs

// <autogenerated />
using UnityEngine;
using UnityEngine.UIElements;

public partial class SampleDocument
{
    [SerializeField]
    private UIDocument _document;

    public Label TitleLabel { get; private set; }

    public Button Button { get; private set; }

    public VisualElement Root => _document?.rootVisualElement;

    public void InitializeDocument()
    {
        TitleLabel = Root?.Q<Label>("TitleLabel");
        Button = Root?.Q<Button>("Button");
    }
}

⚠️ This script behing an auto-generated code based on the UXML template, you should not write code inside this file. It will be overwritten everytime you update your UXML template file.

According to Unity's UI Builder warnings, a VisualElement names can only contains letters, numbers, underscores and dashes. Since a name with dashes is not a valid name within a C# context, during the code generation process, Rosalina will automatically convert dashed-names into PascalCase. Meaning that if you have the following UXML:

<ui:VisualElement>
    <ui:Button text="Button" name="confirm-button"/>
</ui:VisualElement>

Rosalina will generate the following property:

public Button ConfirmButton { get; private set; }

In case you already have a ConfirmButton as a VisualElement name, do not worry, Rosalina will detect it for you during the code generation process and throw an error letting you know there is a duplicate property in your UXML document.

How to use

After installing Rosalina, the code generation process is disabled by default, and should be enabled. Go to Edit > Project Settings > Rosalina and enable it:

image

Once Rosalina is enabled, you will need to manually add a UXML file to the Rosalina's code generation processor. To do so, right-click on a UXML file, go to Rosalina > Properties...:

image

The Rosalina's properties window opens next to the inspector tab, and you can enable it to add the current file to the Rosalina's code generation processor.

image

Rosalina provides three generator types:

  • Document
  • Component
  • Editor

Choose the generator type according to your needs.

When generating a UXML file binding-script, it will be automatically created and will be located at Assets/Rosalina/AutoGenerated and share it's name with a .g.cs extension.

You should NOT edit this file in any way or form, as it will be recreated on each change to the corresponding Visual Tree Asset document.

UI Document

Generator Type: Document

When generating an UI Script, Rosalina will generate the following code:

using UnityEngine;

public partial class SampleDocument : MonoBehaviour
{
    private void OnEnable()
    {
        InitializeDocument();
    }
}

The InitializeDocument() method contains all the UI properties initialization. Every interaction with UI properties such as assigning a text to a label or an clicked event action to a button MUST be defined after the InitializeDocument() call. Otherwise, you will have a NullReferenceException.

using UnityEngine;

public partial class SampleDocument : MonoBehaviour
{
    private void OnEnable()
    {
        InitializeDocument();
        TitleLabel.text = "Hello world!";
        Button.clicked += OnConfirmButtonClicked;
    }

    private void OnButtonClicked()
    {
        TitleLabel.text = "Button clicked";
    }
}

Note, even if this file has been generated, you still can edit it because it will not be overwritten each time you change a UXML file.

Custom components

To use a UI Document as an Unity's Editor Window, set the Generator type in Rosalina's properties window, to Component.

image

Take this UXML file for example:

<ui:UXML ...>
    <ui:Label text="My custom label" name="TitleLabel" />
    <ui:Button text="Sample Button" display-tooltip-when-elided="true" name="SampleButton" />
</ui:UXML>

Rosalina's code generator will generate the following class:

// <auto-generated>
using UnityEngine.UIElements;

public partial class CustomComponent
{
    public Label TitleLabel { get; private set; }

    public Button SampleButton { get; private set; }

    public VisualElement Root { get; }

    public CustomComponent(VisualElement root)
    {
        Root = root;
        TitleLabel = Root?.Q<Label>("TitleLabel");
        SampleButton = Root?.Q<Button>("SampleButton");
    }
}

Then when using this component in another UI Document, Editor or component, Rosalina will automatically initialize it using it's constructor:

<ui:UXML ...>
    <ui:Template name="CustomComponent" src="project://..." />
    <!-- Other UI elements... -->
    <ui:Instance template="CustomComponent" name="MyCustomComponent" />
</ui:UXML>
// <auto-generated>
using UnityEngine;
using UnityEngine.UIElements;

public partial class SampleDocument
{
    [SerializeField]
    private UIDocument _document;

    // Other UI element properties...

    public CustomComponent MyCustomComponent { get; private set; }

    public VisualElement Root => _document?.rootVisualElement;

    public void InitializeDocument()
    {
        // Other initializations...

        CustomLabel = new CustomComponent(Root?.Q<VisualElement>("MyCustomComponent"));
    }
}

In your SampleDocument script, you can access the CustomLabel just like any other UI element or object instance:

using UnityEngine;

public partial class SampleDocument : MonoBehaviour
{
    private void OnEnable()
    {
        InitializeDocument();
        // Setting the TitleLabel's text within the CustomLabel component.
        CustomLabel.TitleLabel.text = "Hello world!";
    }
}

Editor Window

To use a UI Document as an Unity's Editor Window, set the Generator type in Rosalina's properties window, to Editor.

Take this UXML file for example:

<ui:UXML ...>
    <ui:Button text="Sample Button" display-tooltip-when-elided="true" name="SampleButton" />
</ui:UXML>

Rosalina's code generator will generate you a class that extends the UnityEditor.EditorWindow:

// <autogenerated />
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

public partial class CustomEditorWindow : EditorWindow
{
    public Button SampleButton { get; private set; }

    public void CreateGUI()
    {
        VisualTreeAsset asset = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/...");
        VisualElement ui = asset.CloneTree();
        rootVisualElement.Add(ui);
        SampleButton = rootVisualElement?.Q<Button>("SampleButton");
        OnCreateGUI();
    }

    partial void OnCreateGUI();
}

When generating an UI Script, Rosalina will generate the following code:

using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

public partial class CustomEditorWindow : EditorWindow
{
    partial void OnCreateGUI()
    {
    }
}

Every interaction with UI properties such as assigning a text to a label or an clicked event action to a button MUST be defined inside the OnCreateGUI() method:

using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

public partial class CustomEditorWindow : EditorWindow
{
    partial void OnCreateGUI()
    {
        SampleButton.clicked += OnSampleButtonClicked;
    }

    private void OnSampleButtonClicked()
    {
        Debug.Log("Hello world!");
    }
}

If you want, you can also control the behavior of the window and tell Unity how to open it; for instance, with from a Menu Item:

using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

public partial class CustomEditorWindow : EditorWindow
{
    [MenuItem("Window/My Custom Editor Window")]
    public static void ShowWindow()
    {
        EditorWindow.CreateWindow<CustomEditorWindow>("My Custom Editor Window!");
    }

    partial void OnCreateGUI()
    {
        SampleButton.clicked += OnSampleButtonClicked;
    }

    private void OnSampleButtonClicked()
    {
        Debug.Log("Hello world!");
    }
}

This will add a new entry named My Custom Editor Window to the Window menu item:

image

And when clicking on that menu item, the custom editor window will appear and you can now interact with the elements:

image

Notes

As pointed out by JuliaP_Unity on Unity Forums the document initialization process (element queries) should be done on the OnEnable() hook, since the UIDocument visual tree asset is instancied at this moment. Thank you for the tip!

Known limitations

  • The generated files share the name of the Visual Tree Asset. Currently, it's not possible to change the script names.
  • Rosalina currently does not support namespaces.

Final words

If you like the project, don't hesitate to contribute! All contributions are welcome!