Search Unity

Question about built in UIElements Styles

Discussion in 'UI Toolkit' started by Foxaphantsum, Mar 27, 2020.

  1. Foxaphantsum

    Foxaphantsum

    Joined:
    Jul 5, 2013
    Posts:
    139
    Hoes does the built in UIElements styles get applied? I was looking into the built in ObjectField trying to see where it gets its styling but can't seem to figure it out so to help with creating my own custom elements.

    Also I noticed that custom uxml that you create can be used within UIBuilder... which makes me think its possible to design a re-usable element within UIBuilder (like thats intended)

    I was curious if there is a plan to allow for custom UIElements that could be laid out and styled by UIBuilder, but also accessible via code in some intuitive way. I presume you could manually load the uxml file already but was curious if there was some plan to tie this all together somehow.
     
  2. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    Have you had a chance to watch any intro videos or tutorials on UI Toolkit (formally UIElements)? :)

    In C#, you can always create a custom C# element with new and instance a UXML, anywhere, with:
    Code (CSharp):
    1. var uxmlAsset = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(mypath);
    2. uxmlAsset.CloneTree();
    Same goes for the UI Builder. You can find all UXML files in your Project in the Library pane in the lower left, as well as, any custom C# elements (that have the UxmlFactory) under the correct namespace. Both can be dragged over and used within your main UXML document in the Builder.

    As for styles, look for the UIElements Debugger under Window > Analysis. It will clarify where the styles are coming from in general. The UI Builder also has some details on this in its Inspector under the StyleSheet section (look for Matching Selectors).
     
    Foxaphantsum likes this.
  3. Foxaphantsum

    Foxaphantsum

    Joined:
    Jul 5, 2013
    Posts:
    139
    Ooooh! Okay so you can link the two. I was so confused why both existed and what the uses of each separately was.

    To me it seems like you'd mainly use a custom uxml. However you could wrap it with a custom c# element and then more easily instantiate it...(instead of having to do the LoadAsset/CloneTree on the uxml. You can also get the custom attributes added that way too.

    I think I'm understanding how this all comes together now.
     
  4. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    Foxaphantsum likes this.
  5. Foxaphantsum

    Foxaphantsum

    Joined:
    Jul 5, 2013
    Posts:
    139
    Thanks I finally checked out the entire video from start to finish. Really cleared up a lot of stuff.

    I didn't go over an entirely custom UIElement but I feel the CustomInspector info could be good enough for now.

    Thanks this has all been really helpful.
     
  6. Foxaphantsum

    Foxaphantsum

    Joined:
    Jul 5, 2013
    Posts:
    139
    Okay so I've been able to take a template and wrap it with the c# stuff pretty well. The only thing I'm having trouble with is getting the attributes to work correctly. I can't seem to figure out how to add my own custom attributes to this side bar.



    I tried something like this

    Code (CSharp):
    1.         public new class UxmlFactory : UxmlFactory<ToggleItem, UxmlTraits> { }
    2.         public new class UxmlTraits : VisualElement.UxmlTraits
    3.         {
    4.             UxmlStringAttributeDescription _text = new UxmlStringAttributeDescription { name = "text" };
    5.          
    6.             public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc)
    7.             {
    8.                 base.Init(ve, bag, cc);
    9.                 ((Label)ve).text = _text.GetValueFromBag(bag, cc);
    10.             }
    11.         }
    But I just keep getting an error and I'm unsure why.



    This bit of the api is a bit confusing, especially when looking at the internals usage and seeing it not match up with the documentation.

    Like when I see stuff like this in the Toggle

    Code (CSharp):
    1.       public UxmlTraits()
    2.       {
    3.         UxmlStringAttributeDescription attributeDescription = new UxmlStringAttributeDescription();
    4.         attributeDescription.name = "text";
    5.         this.m_Text = attributeDescription;
    6.         // ISSUE: explicit constructor call
    7.         base.\u002Ector();
    8.       }

    Edit* Lastly my custom c# UIElement thats wrapping a custom template element isn't working as inspected.

    I added my custom Element into a new window to test tinting it, and instead of tinting the VisualElement its tinting the background from the template. How do you properly get around this?





    I apologize for all the questions, thank you so much for your amazing support.
     
    Last edited: Mar 28, 2020
  7. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    Here's an example of a custom C# element with exposed custom attributes in the Builder's Inspector:
    https://forum.unity.com/threads/ui-builder-and-custom-elements.785129/

    Attributes are, because of a lack of a better API at this time, reflected by the Builder. In order to work properly, you need to have a C# Property "{ get; set; }" with a certain name that matches (is the camelCaseNoDashes conversion of) the corresponding UXML attribute name. Notice this in the example above. The UI Builder uses these C# Properties to read the current values of these UXML attributes (which are normally just used at instantiation time outside the UI Builder use case.

    The UI Builder actually re-calls the Init() function every time you set a new value in the Builder's Attributes inspector so make sure to set the C# Property there.
     
  8. Foxaphantsum

    Foxaphantsum

    Joined:
    Jul 5, 2013
    Posts:
    139
    Okay thanks! Ill def check this out when Im back on my PC and let you know if I'm able to get it working.

    I wanted to know if you had an answer to my follow up question below about tinting my custom element (and how currently the tint effects the wrong part, it seems to effect the uxml background)

    I was also curious about stuff like text... Is it possible to override the text attribute to work with a label in my custom element?
     
  9. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    Not too sure what you mean by "uxml background", especially for a "custom element". To be clear, "Custom (C#) Element" generally refers to a new type of element, inheriting from VisualElement, that may or may not use some UXML assets to create its internal (private) child hierarchy. Free floating UXML assets are not generally referred to as "custom elements". From the Hierarchy screenshot, I see that you may be referring to a UXML assets you just used inside the Builder. Assuming that's true, are you trying to apply a style (this tint) to a child element?

    Again, I will assume by "custom element" you mean a UXML asset instance. If that's the case, the answer is yes, you can change UXML attributes of elements inside UXML instances from the parent UXML document. Unfortunately, this cannot be done from the UI Builder (and the UI Builder will not properly open files that use this feature, at this time). I would suggest doing this in C#, ideally using a "Custom C# Element".
     
  10. Foxaphantsum

    Foxaphantsum

    Joined:
    Jul 5, 2013
    Posts:
    139
    Sorry for the confusion, I've yet to fully grasp all the names.

    Yea by "custom element" I am referring to the UXML asset instance.

    I've written a Custom C# Element which takes a UXML asset instance.


    C#
    Code (CSharp):
    1.  public class ToggleItem: VisualElement
    2.     {
    3.         public Toggle CheckToggleBox;
    4.         private Label _labelBinding;
    5.         public event Action<ChangeEvent<bool>> changed;
    6.  
    7.         public string Text
    8.         {
    9.             get => _labelBinding?.text;
    10.             set
    11.             {
    12.                 if (!string.IsNullOrEmpty(value)) {
    13.                     if (_labelBinding != null) {
    14.                         _labelBinding.text = value;
    15.                     }
    16.                 }
    17.             }
    18.         }
    19.  
    20.         public bool Value
    21.         {
    22.             get => CheckToggleBox.value;
    23.             set => CheckToggleBox.value = value;
    24.         }
    25.  
    26.         private string _text;
    27.    
    28.  
    29.         public ToggleItem()
    30.         {
    31.             var visualTree = Resources.Load<VisualTreeAsset>("UI/ToggleItem");
    32.             visualTree.CloneTree(this);
    33.  
    34.             CheckToggleBox = this.Q<Toggle>("Toggle");
    35.           _labelBinding = this.Q<Label>("Label");
    36.             _labelBinding.bindingPath = "_text";
    37.             CheckToggleBox.RegisterValueChangedCallback(e =>
    38.             {
    39.                 changed?.Invoke(e);
    40.             });
    41.         }
    42.         public new class UxmlFactory : UxmlFactory<ToggleItem, UxmlTraits> { }
    43.  
    44.     }
    UXML
    Code (CSharp):
    1. <ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements">
    2.     <ui:VisualElement name="ToggleItem" focusable="false" style="flex-direction: row;&#10;background-color: rgba(60, 60, 60, 255);&#10;border-top-left-radius: 2px;&#10;border-bottom-left-radius: 2px;&#10;border-top-right-radius: 2px;&#10;border-bottom-right-radius: 2px;&#10;padding-left: 2px;&#10;padding-right: 2px;&#10;padding-top: 2px;&#10;padding-bottom: 2px;&#10;margin-left: 2px;&#10;margin-right: 2px;&#10;margin-top: 2px;&#10;margin-bottom: 2px;&#10;overflow: hidden;&#10;">
    3.         <Style src="ToggleItem.uss" />
    4.         <ui:Toggle name="Toggle" />
    5.         <ui:Label text="Label" name="Label" style="-unity-text-align: upper-left;&#10;font-size: 14px;&#10;-unity-font-style: bold;&#10;color: rgb(190, 189, 189);&#10;" />
    6.     </ui:VisualElement>
    7. </ui:UXML>
    8.  

    The issue is that when I attempt to tint this, it for some reason wont tint the base VisualElement of the UXML asset, rather the background.

    This is my custom UXML asset that my custom C# Visual Element uses.
    You can see its a rounded VisualElement for the base, with a toggle/label inside.



    Now when I change the background when nesting this element inside another UXML asset, the tint is applied to the background of the UXML asset, rather then the first rooted visual element. (The red is me trying to apply the tint to the base VisualElement of my ToggleItem)

    The dark grey rounded rectangle background should be the one tinted correct? So I guess I'm trying to figure out what I did wrong to get this sorta behavior.
     
  11. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    When you instantiate a UXML instance (via the <Instance /> tag) inside another UXML, you automatically get an extra "TemplateContainer" element. If you change styles in the UI Builder on this element, then that's what's happening. Those styles will not apply to any child (element inside your instance).

    If you want to add styles to child elements, either do that in the original USS tied to your custom C# element ("ToggleItem.uss" in your case), or use the tips from the link below to apply styles on child elements of an instance from the main parent UXML/USS doc:
    https://forum.unity.com/threads/uielements-text-field-changing-child-elements.789656/
     
  12. Foxaphantsum

    Foxaphantsum

    Joined:
    Jul 5, 2013
    Posts:
    139
    Ah okay! This does make sense when you break it down like that.

    However for a builder fan like me, it can come across confusing. I'm curious if their are ways in future to warn against this or build in a way to do this for people...

    Thanks so much for the guidance!
     
  13. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    We'll always be working on making the tool more approachable and discoverable. It's trying to abstract a complex back-end so it will take some time but we'll get there.
     
  14. keil_unity

    keil_unity

    Joined:
    Mar 2, 2021
    Posts:
    28
    does UXML support screen reader accessibility?