[UI Builder] How to make buttons interactable?

Hello, I am using UI Builder.

I would like the functionality where the buttons Tab1, Tab2, Tab3 can be selected (only 1 of them at a time) and depending on which one is selected, the content belows it changes. The tabs are button elements.

5234660--522269--upload_2019-12-2_15-33-52.png

Thank you in advance.

Alex

I would store a variable for the tab index (0 for Tab 1, 1 for Tab 2, 2 for Tab 3), then for the clickable.clicked for each element, I would set the tab index variable to the index of the tab. So you could, if you wanted to, set the userData for each button to the index you want it to be.

Then you would want to update your style for the button. So before you change the tab index, you could revert the style of the old tab index element, then change the tab index, set the style for the new tab element, and then hide the old tab’s container element, and show the new tab’s container element.

Maybe changing the picking mode to ignore and dim the color of button by yourself?

A couple of notes:

  • UI Builder can only really build your UI layout/hierarchy and styles. You still have the use C# to control behavior.
  • Use a different UXML file for each “pane” or tab “container”, then you can work on the layouts individually and at runtime load them all up under your tabs.
  • Once you load all containers (via visualTreeAsset.CloneTree()), you should use style.display = DisplayStyle.None to hide the containers for the tabs that not active.
  • When you register for the Tab buttons clickables, use myTabButton.clickable.clickedWithEventInfo so you can get the EventBase in your callback.
  • You can use EventBase.target to get the Tab button that was clicked (so you can use a single callback for all 3 buttons)
  • In your Tab button callback, just set myElement.style.display = DisplayStyle.None on all containers and then set myElement.style.display = DisplayStyle.Flex on the newly active container.
3 Likes

how to create editor window with
[UI Builder]

Hey! I only just got back to working on this. Thank you very much for the help.

I got up to the point where I registered to the clickedWithEventInfo callback and the EventBase is properly passed but how would I differentiate between the tabs?

Using EventBase.target.ToString() I could maybe get the button’s text and differentiate based on that but is there a better way to get the button’s text in order to differentiate? Or should I not be looking at the button’s text at all but rather some ID? Button position? Something else?

Thank you in advance,

Alex

Edit: EventBase.target.ToString().Contains() is working perfectly but I imagine there must be a better way to differentiate between my tabs.

Edit2: I figured I could do the following: “Button b = tab.target as button” and like that I can get the text which is a cleaner solution. Thank you!

1 Like

Thanks for updating the post after you solved your problem. Always nice to see this. And yes, Edit 2 is one of the recommended approaches.

1 Like

Hello! New developer here. Getting better at navigating UIElements, but by chance is there anyway you can write this out in code so I can better see how this is structured (or should I create a new thread)?

I have been hammering away at google and in code trying to figure out how to get a UI Elements Button/Toggle on a Component Inspector to open up an Editor Window and haven’t been able to piece it together. This thread seems very close to what I am hoping for.

I can show what I have. So in my initialize method I have this.

root.Query<Button>().ForEach((button) =>
{
    button.clickable.clickedWithEventInfo += SwitchTab;
});

Then in the ‘SwitchTab’ method I do this.

5409423--549423--upload_2020-1-26_11-57-54.png

Does that help?

2 Likes

You could even just add a unique class or name to each button and query for them one by one.

rootVisualElement.Q<Button>(className: "tab-button-one").clicked += () =>
{
    SwitchTab(1);
};

Or when looping through multiple buttons you could also use button.ClassListContains("tab-button-one") instead of button.name.

2 Likes

I am gonna feel stupid for admitting this, but it doesn’t help, but maybe I should be a bit more specific too.

Ideally I’d like to have 10 toggles or set of buttons on a custom component inspector that each open up a new, but similar unique instance of an editor window.

I am looking for any documentation on how to call an Editor Window from a Monobehavior script.

Here is our tool: Reddit - Dive into anything

Would love to figure out how to open and close all 10 editor windows via a single individual component/editor (instead of having to access each level window through the Unity toolbar at the top of the screen).

@funcookergames Something like this?

            rootVisualElement.Q<Button>(className: "my-tool-button").clicked += () =>
            {
                var window = EditorWindow.GetWindow<MyWindowType>();
                // Display window
                window.Show();
                window.Focus();
            };

Hello,

So i have tried implementing every way possible to get a click event from my button… so far nothing is working.

Any help would be great thanks.

unity version 2020.1.0a

1 Like

I’m not sure why your clickable is not triggered. Your code looks ok, unless the many registrations conflict each other in some way. This is all you should need. I couldn’t get it not to work for me.

using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

public class SimpleBuggyWindow : EditorWindow
{
    [MenuItem("bla/SimpleBuggyWindow")]
    public static void ShowExample()
    {
        SimpleBuggyWindow wnd = GetWindow<SimpleBuggyWindow>();
        wnd.titleContent = new GUIContent("SimpleBuggyWindow");
    }

    public void OnEnable()
    {
        // Each editor window contains a root VisualElement object
        var root = rootVisualElement;

        var tree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/Button.uxml");
        var treeElement = tree.CloneTree();

        // Do not use .Query() for a single element.
        //Button button = treeElement.Query<Button>();
        // Use .Q():
        var button = treeElement.Q<Button>();

        button.clickable.clicked += () => Debug.Log("Clicked");

        root.Add(button);
    }
}

UXML:

<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements">
    <ui:Button text="Button" style="height: 98px;
width: 110px;
" />
</ui:UXML>

Please see if the exact C# and UXML I have above works or doesn’t work for you. Also try restarting Unity in between. Come back if there’s still an issue.

PS: Please use a code block next time instead of a screenshot. It’s a bit easier for someone to test your code. :slight_smile:

1 Like

@FLDSMDFR2 If you’re using the runtime preview, make sure you have both a PanelRenderer and a EventSystem next to it, otherwise, no mouse or keyboard Events will be fired to your panel’s content, I got bit by that once :slight_smile:

3 Likes

Thank you for the response,

I have updated to use your exact code, but i am still not able to get a click event from within the Game Window. :frowning:

But i do get the click event when clicking the button within the SimpleBuggyWindow EditorWindow

And i do have a UIElementsEventSystem attached

@FLDSMDFR2 Please try this within the Unite Copenhagen Demo project (which has buttons that are clickable and work at runtime):

The runtime support for UI Toolkit is not officially released nor supported so depending on which version of the unofficial code you have you may be running into bugs (that have since been fixed).

1 Like

Thanks again for the response,

I am using the RuntimeDemo Assets and within the Demo Scene the buttons work correctly… So i update my original code to to follow the Demos implementation and it works for me.:slight_smile:

Basically adding a reference to the in game PanelRenderer and called the postUxmlReload.

However if i am not referencing the in game PanelRenderer and try and grab the VisualTreeAsset from the AssetDatabase like so
_assetTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/Button.uxml");
it does not work.

Working Code

using UnityEngine;
using UnityEngine.UIElements;
using UnityEditor;
using System.Collections.Generic;
using Unity.UIElements.Runtime;

public class ShopManagerUIBuilder : MonoBehaviour
{
    //private VisualElement _visualTree;
    //private VisualTreeAsset _assetTree;

    public PanelRenderer GameScreen;

    public void OnEnable()
    {
        GameScreen.postUxmlReload = BindGameScreen;
    }

    private IEnumerable<Object> BindGameScreen()
    {
        var root = GameScreen.visualTree;
        Button b = root.Q<Button>("Button");
        b.clickable.clicked += ButtonClicked;


       //_assetTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>("Assets/Button.uxml");

        //_visualTree = _assetTree.CloneTree();

        //Button b = _visualTree.Q<Button>("Button");

        //b.clickable.clicked += ButtonClicked;

        return null;
    }

    private void ButtonClicked()
    {
        TraceManager.WriteTrace(TraceChannel.Main, TraceType.info, "Button Clicked");
    }
}

UXML

<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements">
    <ui:Button text="Button" name="Button" style="height: 98px;
width: 110px;
">
        <Style src="Button.uss" />
    </ui:Button>
</ui:UXML>

This is an Editor-only API which means that it “might” work at Runtime when running the game in Playmode inside the Editor but it will not work when you build the Player. You can either use the Resources.Load() API or try to reference all the UXML and USS assets you need in a MonoBehaviour at edit time.