Search Unity

UIElements Button

Discussion in 'UI Toolkit' started by vincismurf, Jun 10, 2019.

  1. vincismurf

    vincismurf

    Joined:
    Feb 28, 2013
    Posts:
    200
    I am trying to wrap my head around this concept. So I was wondering, if I make a button like so

    Code (CSharp):
    1.  
    2. VisualElement root = rootVisualElement;
    3. StyleSheet styleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>("Assets/Scripts/Editor/UIElements/ShaderSelector.uss");
    4. VisualElement but = new Button();
    5. but.styleSheets.Add(styleSheet);
    6. but.name = "fred";
    7. root.Add(but);
    8.  
    Is there anyway to add Text to the Button via code? ie Do I have to use .UXML to put in text on the button?

    Please clarify.
     
  2. vincismurf

    vincismurf

    Joined:
    Feb 28, 2013
    Posts:
    200
    Solution

    Code (CSharp):
    1.  
    2. Button b = new Button();
    3. b.text = "hello";
    4. VisualElement but = b;
    5. but.styleSheets.Add(styleSheet);
    6. but.name = "fred";
    7. root.Add(but);
    8.  
     
  3. Nickpofig

    Nickpofig

    Joined:
    Oct 25, 2018
    Posts:
    5
    How to disable(enable) button in new UIElements?

    For example: when "MyInteger" field have value 0 => disable "MyButton" button
     
  4. antoine-unity

    antoine-unity

    Unity Technologies

    Joined:
    Sep 10, 2015
    Posts:
    780
    Try
    button.SetEnabled(myField.value <= 0)
     
    Atheros21 and Stardog like this.
  5. amjadyahya1

    amjadyahya1

    Joined:
    Aug 5, 2016
    Posts:
    12
    How to execute action when UIElement button is clicked?
     
  6. vincismurf

    vincismurf

    Joined:
    Feb 28, 2013
    Posts:
    200
    Ususally it is someButton.Invoke();
     
  7. uMathieu

    uMathieu

    Unity Technologies

    Joined:
    Jun 6, 2017
    Posts:
    398
    button.clickable.clicked += () => Debug.Log("Clicked!");
     
    RKar and leoncorrl like this.
  8. xCyborg

    xCyborg

    Joined:
    Oct 4, 2010
    Posts:
    633
    Hi, I want to add a button to a custom UIElements inpector, I couldn't find it in the docs or the examples repository.
     
  9. uMathieu

    uMathieu

    Unity Technologies

    Joined:
    Jun 6, 2017
    Posts:
    398
    @xCyborg There are multiple ways to do it, via uxml or by code. Here is a quick snippet:
    Code (CSharp):
    1. [CustomEditor(typeof(NewBehaviourScript))]
    2. public class NewBehaviourScriptEditor : Editor
    3. {
    4.     public override VisualElement CreateInspectorGUI()
    5.     {
    6.         var root = new VisualElement();
    7.  
    8.         var b = new Button( () => Debug.Log("Clicked!"))
    9.                     { text = "Click Me!"};
    10.  
    11.         root.Add(b);
    12.         return root;
    13.     }
    14. }
    15.  
     
    Last edited: Aug 20, 2019
    Rowlan and a436t4ataf like this.
  10. xCyborg

    xCyborg

    Joined:
    Oct 4, 2010
    Posts:
    633
    @UnityMat Thank you so much, this is way better than previous imgui workflow.
    On a side note, didn't you mean to return the root visual element at the end?

    Code (CSharp):
    1.         return root;
    Also, would you be so kind as to show me how to display the underlying script properties in the inspector as usual?
    I tried calling some base functions and adding targets property to the root but it didn't work.

    Also I would like to know how to access those propertied from the custom inspector.

    Thanks in advance so much.
     
  11. xCyborg

    xCyborg

    Joined:
    Oct 4, 2010
    Posts:
    633
    ?? bump
     
  12. uMathieu

    uMathieu

    Unity Technologies

    Joined:
    Jun 6, 2017
    Posts:
    398
    @xCyborg Sorry about the late answer, I just returned from vacation :)

    You're right, I meant returning root in my code snippet, fixed it in my original post.

    An Editor has a serializedObject property that let's you list serialized properties. Here is how you could populate your inspector:
    Code (CSharp):
    1. SerializedProperty property = serializedObject.GetIterator();
    2. property.NextVisible(true); // Expand the first child.
    3. do
    4. {
    5.     var field = new PropertyField(property);
    6.  
    7.     root.Add(field);
    8. }
    9. while (property.NextVisible(false));
    You can also create your own fields and set their binding path to the names of the edited object's fields:

    Code (CSharp):
    1. var field = new IntegerField();
    2. field.bindingPath = "IntValue";
    3. root.Add(field);
    Once returned, the created VisualElement tree will be bound to the serialized object.

    Hope that helps!
     
  13. xCyborg

    xCyborg

    Joined:
    Oct 4, 2010
    Posts:
    633
    @UnityMat Thanks Mat and welcome back.
    I used your snippet but I find using serializedObject way convoluted for the simple task I aim to.
    I've gathered some feedback ans I hope you take a look into it.

    -First it requires iteration and dynamic querying,
    -Displaying some attributes like [Space] behave weirdly or don't function.
    -Also I'd still need to query individual properties if I need to reference them again.
    -Lastly, assigning or modifying serialized properties from UIElements callback doesn't seem to work.

    Code (CSharp):
    1.  
    2.        var d = serializedObject.FindProperty("deutch"); // default value = "Empty"
    3.  
    4.         // ADD MY STUFF
    5.  
    6.         var b = new Button(() =>
    7.         {
    8.             Debug.Log(d.stringValue);
    9.             d.stringValue = "Deutch!";  // <=== Value doesn't change in inspector nor in debug mode.
    10.         })
    11.         { text = "Click Me Deutch!" };
    Could there be a way to automatically draw the public properties of the custom editor's backing class? or at least wrap it in a single function call like : DrawDefaultInspector()...
    And more importantly be able to reference all the fields and properties of the inspected class in each object directly.

    Is that possible? whether sooner or later, it would be a huge boost for editor scripting.
    Thanks.
     
    Last edited: Aug 24, 2019
  14. xCyborg

    xCyborg

    Joined:
    Oct 4, 2010
    Posts:
    633
    EDIT: I was able to enforce the property change by using :
    Code (csharp):
    1.   serializedObject.ApplyModifiedProperties()
    But still, most of the things I mentioned stand.
    It would be far better if we could access the underlying inspected object directly without the`serializedObject` mess, can't even call GetComponent from Editor scripts.
    And also to easily OR automatically draw the public variables of the inspected script.

    Thanks again.
     
  15. xCyborg

    xCyborg

    Joined:
    Oct 4, 2010
    Posts:
    633
    EDIT #2:
    I remembered target property, it works wonderfully, now I got rid of all serialized stuff complication.
    So the reference issue is no longer an issue now.

    That leaves us with only the need for a DrawDefaultInspector() counterpart in UIElements.
    Maybe you could add an overloaded method in Editor that takes a VisualElement and does something like what you posted above.

    Code (CSharp):
    1.     private void DrawDefaultInspector(VisualElement root)
    2.     {
    3.         SerializedProperty property = serializedObject.GetIterator();
    4.         property.NextVisible(true); // Expand the first child.
    5.         do
    6.         {
    7.             var field = new PropertyField(property);
    8.  
    9.             root.Add(field);
    10.         }
    11.         while (property.NextVisible(false));
    12.     }
     
  16. uMathieu

    uMathieu

    Unity Technologies

    Joined:
    Jun 6, 2017
    Posts:
    398
    The need for a default UIElements inspector is definitely there and it's on our todo list!
     
    tonytopper likes this.
  17. xCyborg

    xCyborg

    Joined:
    Oct 4, 2010
    Posts:
    633
    Awesome! glad to hear it, thanks again for the tip and I hope we get some documentation on UIElements, it's a brilliant system.
     
  18. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    Is there any way to do this without a method? It should not be a method, it should be a property - as a method, it's impossible to use C# standard constructor syntax, e.g. this code is impossible:
    Code (CSharp):
    1. element.Add( new Button(()=>{onclick();}) { text = "Button", isEnabled = /* not possible :( */ } );
     
  19. sebastiend-unity

    sebastiend-unity

    Unity Technologies

    Joined:
    Nov 9, 2015
    Posts:
    184
    I agree this is not ideal. Not at the moment I am afraid.
     
  20. a436t4ataf

    a436t4ataf

    Joined:
    May 19, 2013
    Posts:
    1,933
    OK, for now I'm using a workaround - a static class that adds a method to UIToolkit:

    public static void AddButton_WorkaroundUIToolkitMissingAPI( this VisualElement e, onclickHandler, string text, bool isDisabled = false ) {..}

    ...unless anyone has better ideas?

    It works and it allows us to keep instantiating buttons in a single line of code, but ... it's not ideal because it disables access to C#'s constructor-initialization system :( - I'll have to add a lot of extra duplicate methods to accomodate all the different parameters you could add to a Button, e.g. every style change etc - but at least I can delete it if/when UIToolkit adds the appropriate property.