Search Unity

How to make a clone/copy of a VisualElement?

Discussion in 'UI Toolkit' started by Vapid-Linus, Aug 7, 2019.

  1. Vapid-Linus

    Vapid-Linus

    Joined:
    Aug 6, 2013
    Posts:
    64
    How do I clone a VisualElement?

    Like,
    newElement = oldElement.Clone();
    or
    newElement = new VisualElement(oldElement);
     
    Aldeminor, DragonCoder and tonytopper like this.
  2. jonathanma_unity

    jonathanma_unity

    Unity Technologies

    Joined:
    Jan 7, 2019
    Posts:
    229
    Hi,

    There's no way to clone a VisualElement.
    What you can do instead is to create a new VisualElement and then set the same USS classes, event handlers and so on.
     
  3. Vapid-Linus

    Vapid-Linus

    Joined:
    Aug 6, 2013
    Posts:
    64
    Thank you for the reply.

    The thing is that the visual element has several nested children as well. Manually copying everything is pretty tedious. I guess a method that manually copies everything could be created, but I feel a simple copy/clone method should be part of the standard API. I can think of several use cases for it and feel that will be a fairly common request.

    Is the planned? If not, why not?
     
  4. jonathanma_unity

    jonathanma_unity

    Unity Technologies

    Joined:
    Jan 7, 2019
    Posts:
    229
    To my knowledge this is the first time that a request for VisualElement cloning comes.
    I don't think this is planned, but I'll start a discussion about it.

    Meanwhile if you want to clone hierarchy of elements I'd recommend using UXML with VisualTreeAsset.CloneTree. This way you can create a tree of elements with the same set of properties.

    A clone function may sound really simple on the surface, but it actually isn't. There are many internal properties to a VisualElement and some of them are trivial to clone and others are not or must not be cloned at all. There's also the case of element inheriting from VisualElement...
     
    Vapid-Linus likes this.
  5. Stardog

    Stardog

    Joined:
    Jun 28, 2010
    Posts:
    1,913
    From my testing, it's best to use an MVC pattern with a Clone function on the model it will automatically deliver the correct VisualElement from your view.
     
  6. Vapid-Linus

    Vapid-Linus

    Joined:
    Aug 6, 2013
    Posts:
    64
    I appreciate your reply and your explanation, thank you. That makes a lot of sense.

    I understand that visualTreeAsset.CloneTree is the only way to create/clone elements without creating them through C#. My problem is that there are a lot of different element-trees that I want to clone/copy for lists and such. I do not wish to create those manually with code as they are quite complex and I want to iterate quickly, and having to create different UXML files for every element-tree I want to clone will create a lot of UXML files which is a bit tedious. Is there a way (or is it even feasible) to use CloneTree on just a part of a tree asset? Like querying (by class or name) for a specific element within a tree asset and creating a clone of just that element?

    Thank you for your time.
     
  7. jonathanma_unity

    jonathanma_unity

    Unity Technologies

    Joined:
    Jan 7, 2019
    Posts:
    229
    From what I understand this statement is contradictory, if the element-trees are not created from UXML they must have been created manually from code. If that is the case it's just a matter of creating some functions that would create the desired hierarchy and reuse that code where needed.

    But to answer your question CloneTree works on a full VisualTreeAsset only.

    Finally, may I suggest that you try to approach your problem in a different way?
    There are many other options to VisualElement cloning, I don't see any compelling reasons to do so personally but I may be missing something. And if it's not enough you still have the possibility of creating your own cloning function...
     
  8. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    1,419
    This seems like a shame... Can these be nested?

    UI stuff really needs the ability to be swappable with other elements. I should be able to take a whole form within an EditorWindow and swap it out with a different form....



    I'm already very hesitant on VisualElements and this isn't helping... :(
     
  9. jonathanma_unity

    jonathanma_unity

    Unity Technologies

    Joined:
    Jan 7, 2019
    Posts:
    229
    This is totally possible with UIElements, you can modify the tree returned by CloneTree as much as you want.
     
  10. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    1,419
    That's good to know -- I was assuming it would change the original.

    This makes me feel a bit better about it.
     
  11. Creta_Park

    Creta_Park

    Joined:
    Mar 11, 2016
    Posts:
    76
    Might be near of topic, how about use instead override class to create layout itself?
    VisualElement
    is not sealed class, so you can override it.

    Code (CSharp):
    1. public class MyCustomElement : VisualElement
    2. {
    3.     public MyCustomElement() : base() {
    4.         //TODO : Create layout
    5.         this.AddManipulator(new MyCustomElementEvent());
    6.     }
    7.  
    8.     class MyCustomElementEvent : Manipulator
    9.     {
    10.         //TODO : Register, Unregister event and write event's logic.
    11.     }
    12.  
    13.     //Sure if you want to clone data from some source...
    14.     public void CopyFrom(MyCustomElement source) {
    15.         //TODO : copy value, layout data from source...
    16.     }
    17. }
    'UIElements base class is overridable' means lot of possibilities to customization, so able to make own scripted, functional element.
    You should try with it.

    I had with this possibilities.
    https://creta5164.tumblr.com/post/187093161546/second-week-of-20198-white-spirit-dev-status

    ME3MG8d.gif
     
    awesomedata likes this.
  12. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    Although we encourage encapsulation where possible, VisualElement was made to be inherited from and overridden. This is how you make custom elements with custom behavior, like a TreeView.
     
    Aldeminor and Creta_Park like this.
  13. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    1,419
    Can you get someone to do this override magic for the SceneCameraController so that I can make my own SceneCameraController for the SceneView please?

    I want my orbit behavior to work differently than the standard Unity one...
     
  14. Suduckgames

    Suduckgames

    Joined:
    Nov 28, 2016
    Posts:
    218
    I will like to support the clone function on a visual element. I am new to UI Elements and I may be doing something wrong or thinking it in a wrong way, so feel free to correct/guide me.

    Coming from the android background. I am trying to do a dynamic ListView and separating the adapter .uxml files In order to reutilize them in different list views. So each time that I am using them in a list view I need to first create a clone to know the height of the layout. And then each time that a new item is required, create a copy of the adapter file from the VisualTreeAsset.

    Is that the way it is meant to work? In my head, I want to get a concrete visual element and N copies of it. Instead of copy the whole file. If I only want to populate a listview with part of the adapter I should be able to create copies of the specific part. If not, I am forced to separate each part of the adapter in each file and build them manually or lookup for the specific part each time that a new object is required.

    Is that correct? I am missing something?
     
  15. Refeas

    Refeas

    Joined:
    Nov 8, 2016
    Posts:
    192
    As a webdev I really like the way UI Toolkit is done and I see a lot of people struggle with the concept in some ways. Hope this will shed some light on the concept.
    Try to imagine the UXML assets as templates/components/views. So, let's say you want a list of players. So you create the base template containing the layout, the ListView, etc. Then you want to design each entry of the player list. So you go ahead and create some playerentry.uxml asset, design that the way you'd like the player entries to look in the list. And then, via script, you set the itemSource, makeItem and bindItem functions of the ListView. In the makeItem function, you can use the CloneTree method on the playerentry.uxml asset which will populate the list of players with your designed entries automagically. Then you can use the bindItem function to set the proper data values on the playerentry subelements.

    TL;DR: I think creating multiple UXML assets (for base templates, specific child items etc.) is the proper way to go as it will separate neccessary views and will be more organized in a long run. Didn't really think of a need for Clone function as of yet.
     
    DragonCoder likes this.
  16. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    Just to shed more light, as Refeas above mentione, UXML is the "clone" feature you are looking for. The C# equivalent is the standard "C# function" (you can always create a function that creates a specific set of element in C#, so kinda like a "clone"). For something like ListView, indeed, you'd want two UXML files, one for the main UI that contains the ListView and one for the "template" of each of the items.

    As for why it's not possible to clone a C# VisualElement. This is because elements are C# Objects. They have non-trivial constructors which can often create more child elements, state that cannot be serialized like C# Properties, references to other VisualElements, and often registered callbacks made by interested parties. Also VisualElements have no unique identifiers so if one VisualElement references another, this reference is only stable at runtime while those two elements exist as Objects. It needs to be recreated when the elements are recreated.

    While it would be nice to be able to "clone" a UI element, it would prevent us from having the advantages we have now that they are actual C# objects. Hence, to get something close to a clone, we implemented the UXML approach.
     
  17. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    The UI Elements Debugger clearly shows that you're able to convert the C# objects to uxml. You list all the VisualElements and all their classes, names, inline properties, etc. You can pretty trivially build uxml from that.

    At the same time, you're obviously able to generate the VisualElement objects from uxml.

    So it should not be hard to implement clone through first generating the uxml from the VisualElement, then creating new VisualElements from that uxml.


    Doing that approach would also have two huge benefits:
    - It'd really be nice to output the generated uxml, both for debugging and for editor tools that wants to build UI.
    - It'd be really nice at times to generate VisualElements from a uxml string rather than an asset. That'd allow things like implementing a newsfeed in your main menu simply by downloading the uxml/uss text from a url.

    Does the uxml asset wrapper contain some kind of accelerator/serialized version of the generated objects rather than just the text? If it just parses the uxml file from text at runtime, the only thing keeping this from already working is access modifiers on Unity internals, but it could be more complex, idk.
     
  18. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    This is not an accurate conversion. It's just a bit of help to have something to start with. It may not even be valid UXML. It just reflects the VisualElement types and generates UXML <tags> with the full type name. A lot of information is lost.

    As I explained above, UXML -> VisualElements is a one-way conversion, where the end result has more information and state than can be represented in UXML. Going back from VisualElement -> UXML is not generally possible for the same reason why compiled object files cannot be converted back to source code. Yes, you "can" do an approximated conversion, with lots of exceptions and pitfalls, and you are free to do that for your own code in the same way the UI Debugger does it (if you get the UI Toolkit package, the Debugger code is there so you can see how it generates that UXML). But we won't release such a feature because the expectation will be that it works "all the time" and reliably.

    This will be possible at some point. We just didn't make the function you need public. It's a method on our UXML importer that we use after we read the contents of the .uxml file. UI Builder converts UXML to VisualElements in memory all the time. If you copy some elements in the Builder and you paste in a text editor, you'll see that what you copied was UXML. The UI Builder can generate UXML in this case because it uses the main asset UXML you have open as the source, not the live VisualElements you see in the work area. It just extracts the bit of UXML from main asset.

    Yes, the VisualTreeAsset, which is the C# object generated from importing a text-based .uxml file, is indeed optimized for runtime use. We do not parse UXML at runtime by default for performance reasons.
     
  19. SugarChap

    SugarChap

    Joined:
    Jan 19, 2015
    Posts:
    28
    Hi there.

    Instead of cloning, I'm happy to build from an UXML, but I'm not keen on having hand coded paths kicking around, or lots of hand defined references - I'd rather find it within an existing scene. It seems to me that an ideal bridge would be to:
    1. Define the object that I would want to clone from a UXML (let's call that UXMLCreatedObject)
    2. Place it within my heirarchy (another UXML) defining my screen as normal
    3. In c#, find the UXMLCreatedObject.
    4. Dig out it's source UXML file
    5. Create another one.
    Is this possible?
     
  20. antoine-unity

    antoine-unity

    Unity Technologies

    Joined:
    Sep 10, 2015
    Posts:
    780
    So if I understand correctly, you would like to centralize all of your asset references in one place ? Basically only have a reference to one asset which internally serves as the library for all object types to create ?

    If that's so, I would recommend looking into creating a custom Scriptable Object which accomplishes the same job:

    Code (CSharp):
    1. [CreateAssetMenu(menuName = "My Assets/TVisualTreeAsset References")]
    2. public class VisualTreeAssetReferences : ScriptableObject
    3. {
    4.     public VisualTreeAsset ref1;
    5.     public VisualTreeAsset ref2;
    6. }
    Then create an instance of that and manage an "hardcoded" path to it. If you're working with game UI you can just pass a reference to it.

    Then in the Inspector you can just drag and drop references to your different UXML files, use VisualTreeAsset.CloneTree() as described above.
     
    florianhanke likes this.
  21. SugarChap

    SugarChap

    Joined:
    Jan 19, 2015
    Posts:
    28
    Hi there, thanks for the idea.

    That is certainly something that I will use - as a fall back. But no, I don't want to centralise the assets into a single place. I'd very much like to get the VisualTreeAsset/UXML directly from the VisualTreeAsset that it will be replicated within.

    The reason that I would like to do this is so that I can find the appropriate UXML for the context. i.e. One Screen has it's own UXML for an element (in it's hierarchy, so that we can see it easily in context), while another has another (or even different places in the same screen).

    upload_2020-10-8_14-1-37.png

    For example, in the element 'ListA' are three other gameobjects that are used as 'prefabs' (this is the old Unity GUI system). I can find these, duplicate them and populate the list with the appropriate ones. Other screens can have their own prefabs (should they wish).

    This is a model of UI development that has been handy for a previously released title using the previous unity gui, but the ability to find a visual element and replicate it (using UXML is fine) is important for this - especially as the project grows - our game project has >20 different screens, and they often have their own flavour of common elements.
     

    Attached Files:

  22. ProgrammerSi

    ProgrammerSi

    Joined:
    Mar 1, 2016
    Posts:
    4
    I currently have the exact same problem...

    I am using the UIBuilder to create a tool, and i want to be able to visualise the entire tool in one uxml but also want to be able to clone placeholder VisualElements.

    I see the way to do this is to nest different uxmls, which is fine, but i would also want to be able to duplicate these nested uxml in code from only the original main uxml.
     
  23. antoine-unity

    antoine-unity

    Unity Technologies

    Joined:
    Sep 10, 2015
    Posts:
    780
    Thanks, I understand better now. So would a workflow like this work for you:

    1) Create a template like MenuItem.uxml
    2) Use one instance of in another file, say Menu.uxml
    3) Give an name to the template instance
    3) As usual with UI Toolkit, use uQuery to find the instance by name
    4) Use some kind of API that gives you the VisualTreeAsset where the visual element originates from ?

    Or alternatively:

    1) Create a template like MenuItem.uxml
    2) Use one instance of in another file, say Menu.uxml
    3) From the VisualTreeAsset asset object that represents "Menu", use some kind of API to retrieve a direct reference to the VisualTreeAsset to "Menu" (again, by name, but the name being the template name in this instance)
     
    SparkesRS likes this.
  24. SugarChap

    SugarChap

    Joined:
    Jan 19, 2015
    Posts:
    28
    I understand this, and like it.

    I'm afraid I'm not sure I understand this one. If it's to do with finding the menu item by template name (ie. MenuItem.uxml) then this could go wrong if for example I swap out the MenuItem.uxml for another named one i.e. MenuItemBold.uxml.

    The good thing about getting the visual tree asset is that I can find it just like any other visual element (by name, or some other means) and then even if I switch out the .uxml to another one, as long as I can find it the same way, everything is fine - no part of my code has links to an asset name. We can change them out artist side without code change.

    Of course, that is only if I'm understanding correctly.
     
  25. SugarChap

    SugarChap

    Joined:
    Jan 19, 2015
    Posts:
    28
    So... Is this possible at the moment?
     
  26. antoine-unity

    antoine-unity

    Unity Technologies

    Joined:
    Sep 10, 2015
    Posts:
    780
    Sorry I should have clarified that this is not possible yet.
    I just wanted to bounce some ideas with you, it looks like the first proposal would suit your needs.

    Since this seems like a low hanging fruit I'll bring it up to the team and get back to you.
     
    awesomedata likes this.
  27. SugarChap

    SugarChap

    Joined:
    Jan 19, 2015
    Posts:
    28
    Wonderful. Thanks very much for the support.
     
  28. SugarChap

    SugarChap

    Joined:
    Jan 19, 2015
    Posts:
    28
    @antoine-unity Hi! Necroing this thread to see if this functionality (or something that achieves the same) is planned? Would really help me to plan our tech.

    Thanks!
     
  29. antoine-unity

    antoine-unity

    Unity Technologies

    Joined:
    Sep 10, 2015
    Posts:
    780
    Hello, it looks like we will be able to squeeze this in the 1.1 version cycle of the package which will end up shipping 21.2.

    The 1.1 experimental version should hopefully become available in January 2021 and will work with 2020.2 and up.
     
    SugarChap likes this.
  30. SugarChap

    SugarChap

    Joined:
    Jan 19, 2015
    Posts:
    28
    That's excellent. Thanks!
     
  31. JuliaP_Unity

    JuliaP_Unity

    Unity Technologies

    Joined:
    Mar 26, 2020
    Posts:
    700
    Hey there, more recent update: it is now possible to locate the UXML (as a
    VisualTreeAsset
    used for cloning a tree into a Visual Element from code and using that to clone into other Visual Elements on Unity 2021.2.

    The new API provides 2 new properties to allow access to UXML information:
    • VisualElement.visualTreeAssetSource
      - Stores the asset reference, if the generated element is cloned from a VisualTreeAsset.
    • TemplateContainer.templateSource
      - Stores the template asset reference, if the generated element is cloned from a VisualTreeAsset as a Template declaration inside another VisualTreeAsset.
    What this means is that whether you clone a UXML from code, or use a reference on your UXML with a
    Template
    directive, you have a way to find the reference and use it elsewhere in the code. We needed to add both properties to cover all possible cases and make sure we deliver on the promise of being able to access that UXML somehow.

    Let me know if this is not clear and I can cook up an example :)
     
  32. SugarChap

    SugarChap

    Joined:
    Jan 19, 2015
    Posts:
    28
    Hello! Apologies for the late response. YES PLEASE - an example would be fantastic.
     
    awesomedata likes this.
  33. tankorsmash

    tankorsmash

    Joined:
    Jul 20, 2019
    Posts:
    7
    I was able to get 'cloning' done by at first using
    AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(uxml_path).Instantiate()
    , but then I realized it was editor only. Using
    my_uidoc_component.visualTreeAsset.Instantiate()
    worked as a drop-in replacement now, on unity 2021.1 from what I can tell.

    Maybe it was backported from 2021.2, but I'm new to Unity and don't fully understand how updates rollout anyway.

    Thanks for the answer everyone!
     
  34. RunninglVlan

    RunninglVlan

    Joined:
    Nov 6, 2018
    Posts:
    182
    Could you please provide examples? I don't yet fully understand if it works for my use case.
    In uGUI I could store the "prefab" (child) inside another prefab (container). The child could be disabled, but when Instantiated I would enable its Instances. Or it could be enabled, but I would disable or remove it at Runtime after Instantiating its Instances.
    Is it possible in UITK (maybe now with this new API) or we always need to have separate UXML assets for that?
     
  35. JuliaP_Unity

    JuliaP_Unity

    Unity Technologies

    Joined:
    Mar 26, 2020
    Posts:
    700
    Hey @SugarChap and @RunninglVlan sorry it took me so long to come back and answer but I cooked up an example that I hope will be helpful :)

    I prepared these 2 UXMLs that are very simple and have just enough visuals for making an example you can see on your Game View and make sense of.

    This is the Main.uxml file, which I'll use on my UIDocument:
    Code (CSharp):
    1. <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">
    2.     <ui:Template name="Sub" src="project://database/Assets/Sub.uxml?fileID=9197481963319205126&amp;guid=031f00079eabc45a48a014c7db7ce240&amp;type=3#Sub" />
    3.     <ui:VisualElement>
    4.         <ui:Label text="My Title Label" display-tooltip-when-elided="true" />
    5.         <ui:Instance template="Sub" name="Sub" />
    6.     </ui:VisualElement>
    7. </ui:UXML>
    And this is the Sub.uxml file, which I use inside my Main.uxml through the
    Template
    declaration (on line 2 above, and then used through the
    Instance
    declaration on line 5):
    Code (CSharp):
    1. <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">
    2.     <ui:TextField picking-mode="Ignore" label="Text Field" value="filler text" text="filler text" />
    3.     <ui:Slider picking-mode="Ignore" label="Slider" value="42" high-value="100" />
    4.     <ui:Button text="Button" display-tooltip-when-elided="true" />
    5. </ui:UXML>
    In my scene I added a UIDocument and set the Main.uxml as the source asset and added this other component to the same game object. The comments in the code explain the use of each property as well as what you can do with the references retrieved:
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UIElements;
    3.  
    4. // Place this component in the same Game Object as the UIDocument component in the scene.
    5. public class UIDocumentCompanion : MonoBehaviour
    6. {
    7.     private void OnEnable()
    8.     {
    9.         var uiDocument = GetComponent<UIDocument>();
    10.  
    11.         // The first child of the root comes from the Instantiate() call of the UXML set as the "Source Asset" (Main) and...
    12.         var mainContainer = uiDocument.rootVisualElement[0];
    13.        
    14.         // ... because of that, its "visualTreeAssetSource" reference is set to be the UXML assigned to "Source Asset" (Main).
    15.         var mainUxml = mainContainer.visualTreeAssetSource;
    16.        
    17.         // Inside the UXML assigned to "Source Asset" (Main), there's the use of a template which can be easily found through the type "TemplateContainer" used in the process of cloning this other UXML (Sub).
    18.         var subTemplateContainer = mainContainer.Q<TemplateContainer>();
    19.  
    20.         // Since this is the element into which the UXML was copied into, it holds the reference to it in "templateSource" (Sub).
    21.         var subUxml = subTemplateContainer.templateSource;
    22.        
    23.         // And the UXML assigned to "Source Asset" (Main) can be found the same way in the root element as it is a TemplateContainer itself.
    24.         var mainUxmlAgain = (uiDocument.rootVisualElement as TemplateContainer).templateSource;
    25.  
    26.         // Also we can access the UXML used as template (Sub) from the first child of "subTemplateContainer", similar to what we did for the UXML assigned to "Source Asset" from the root element.
    27.         var subUxmlAgain = subTemplateContainer[0].visualTreeAssetSource;
    28.        
    29.         // Now if you  wanted to have more instances of the Sub UXML in your Main part, you can just instantiate more like this:
    30.         mainContainer.Add(subUxml.Instantiate());
    31.        
    32.         // Or if you wanted to have more instances of the Main UXML within your UIDocument, you could add them like this:
    33.         uiDocument.rootVisualElement.Add(mainUxml.Instantiate());
    34.     }
    35. }
    I took this screenshot of my Unity running with these UXMLs and the companion class I copied here and this is the result. The Debugger shows the parts created in the process, the elements highlighted in red come from the original load of the Main.uxml file by the UIDocument component, which generates the TemplateContainer called "UIDocument-container". Then highlighted in yellow is the additional copy of Sub.uxml done on line 30 of the script (pointing to the corresponding element in the tree). After that I highlighted in blue the additional copy of Main.uxml done on line 33 of the script (pointing to the corresponding element in the tree): visualtreeassetsourceexample.png
     
  36. pablobandinopla

    pablobandinopla

    Joined:
    Jul 2, 2021
    Posts:
    98
    it would be very useful if the CloneTree method could return the reference to the created element...

    Like if i clone a template container that has a button in it, on a loop, i would like to quickly be able to add a listener to the clicked delegate...

    is this possible already? (i just started learning the new UIBuilder workflow...)

    This is how im currently getting the reference to the newly added element:
    Code (CSharp):
    1. optionsAsset.CloneTree(optionsRoot, out int itemIndex, out int elemCount );
    2. VisualElement newOption = optionsRoot.ElementAt(itemIndex);
     
    Last edited: May 17, 2022
    awesomedata likes this.
  37. JuliaP_Unity

    JuliaP_Unity

    Unity Technologies

    Joined:
    Mar 26, 2020
    Posts:
    700
    If you call
    optionsAsset.Instantiate()
    it returns the instance as a
    TemplateContainer
    type, and you can then add that to your visual tree (
    optionsRoot
    ). Also if you use different Visual Element instances to clone into (i.e. calling
    optionsAsset.CloneTree(optionsPart)
    ), and then add those to the visual tree (
    optionsRoot
    ), that particular Visual Element can be used for the purpose to add callbacks, etc.

    Hope this helps! :)
     
    RunninglVlan likes this.
  38. pablobandinopla

    pablobandinopla

    Joined:
    Jul 2, 2021
    Posts:
    98
    Nice! thanks!

    Code (CSharp):
    1. TemplateContainer optionUI = optionsAsset.Instantiate();
    2. optionsRoot.Add(optionUI);
     
  39. DREBOTgamestudio

    DREBOTgamestudio

    Joined:
    Jan 30, 2018
    Posts:
    66
    I don't understand how to copy elements. I have DialogueTextItem. I put in my DialogueScreen. How to make a copy of DialogueTextItem?
    upload_2022-7-15_16-46-44.png
    I can find DialogueTextItem. But i can not copy it.
    Code (CSharp):
    1. GetComponent<UIDocument>().rootVisualElement.Q<VisualElement>("dialogue-text-item");
    I try to get visualTreeAssetSource from DialogueTextItem but dItem is null.
    Code (CSharp):
    1. var dItem = GetComponent<UIDocument>().rootVisualElement.Q<VisualElement>("dialogue-text-item").visualTreeAssetSource;
     
  40. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    If I'm reading the code correctly, you might be getting null from visualTreeAssetSource because "dialogue-text-item" is the TemplateContainer. This is the parent for everything cloned from DialogueTextItem.uxml, but it isn't itself part of DialogueTextItem.uxml, so it doesn't have a visualTreeAssetSource. You need to look inside.
     
    DREBOTgamestudio likes this.
  41. kiberptah2

    kiberptah2

    Joined:
    Apr 1, 2020
    Posts:
    2
    Thanks a lot, it's actually very helpful. I wish Unity had non-outdated tutorials explaining this things!
     
    awesomedata likes this.
  42. DragonCoder

    DragonCoder

    Joined:
    Jul 3, 2015
    Posts:
    1,700
    Have to second the request of this thread. A clone method would be really helpful in certain cases.

    Have tried the suggested approach to create a new element and then assign the values with code, but somehow according to the debugger, code like this does not seem have any effect at all:

    Code (CSharp):
    1.  
    2. private void MakeALikeB(VisualElement A, VisualElement B)
    3.     {
    4.         A.style.position = B.style.position;
    5.         A.style.top = B.style.top;
    6.         A.style.bottom = B.style.bottom;
    7.         A.style.left = B.style.left;
    8.         A.style.right = B.style.right;
    9.  
    10.         A.style.flexGrow = B.style.flexGrow;
    11.         A.style.flexShrink = B.style.flexShrink;
    12.         A.style.width = B.style.width;
    13.         A.style.height = B.style.height;
    14.         A.style.transformOrigin = B.style.transformOrigin;
    15.     }
    A and B retain different values after executing this.

    My usecase by the way is to simply dynamically overlap multiple background images on the same spot.
    If I do "Ctrl+D" to duplicate the element in the editor, it works flawlessly.

    EDIT:
    Ok, nevermind. Apparently I gotta take "resolvedStyle" instead of "style" from object B to actually get the current values.
    Then the code above works.
     
    Last edited: Nov 1, 2022
  43. tree_arb

    tree_arb

    Joined:
    Dec 30, 2019
    Posts:
    323
    This might be limited but I was able to clone what I needed this way:

    Code (CSharp):
    1.  
    2. public void CloneBG(VisualElement original)
    3.         {
    4.             VisualElement bg = new VisualElement();
    5.  
    6.             IList<string> classList = original.GetClasses() as IList<string>;
    7.  
    8.             int c = classList.Count;
    9.             for (int i= 0; i < c; i++)
    10.             {
    11.                 bg.AddToClassList(classList[i]);
    12.                 Debug.Log(classList[i]);
    13.             }
    14.             parent.Add(bg); // add it where you want
    15.         }
    where the "original" visual element being sent in to the function to be cloned from is created in ui builder
     
    Hexane likes this.
  44. Xepherys

    Xepherys

    Joined:
    Sep 9, 2012
    Posts:
    204
    The best thing I've found when creating VisualElements in code is to create a class that will return the elements that you need, especially if you need to create the same element in multiple places.

    For instance, this won't work:


    Code (CSharp):
    1. VisualElement ve = new VisualElement();
    2. TextField tf = new TextField();
    3. ve.Add(tf);
    4.  
    5. root.Add(ve);
    6. root.Add(ve);
    You will only get the last `ve` inserted.

    In my case, I figured this out having created a divider element group that just created space, a divider, and space again. Added a class for VisualElementExtensions with the following:


    Code (CSharp):
    1.  
    2.         public static VisualElement SectionDivider(float spacerHeight = 10f, float dividerHeight = 3f)
    3.         {
    4.             VisualElement sectionDivider = new VisualElement();
    5.             sectionDivider.Add(Spacer(spacerHeight));
    6.             sectionDivider.Add(Divider(dividerHeight));
    7.             sectionDivider.Add(Spacer(spacerHeight));
    8.  
    9.             return sectionDivider;
    10.         }
    11.  
    12.         public static VisualElement Spacer(float height = 10f)
    13.         {
    14.             VisualElement spacer = new VisualElement();
    15.             spacer.style.height = height;
    16.             return spacer;
    17.         }
    18.  
    19.         public static VisualElement Divider(float height = 3f)
    20.         {
    21.             VisualElement divider = new VisualElement();
    22.             divider.style.backgroundColor = new Color(0.157f, 0.157f, 0.157f);
    23.             divider.style.borderTopWidth = divider.style.borderBottomWidth = divider.style.borderLeftWidth = divider.style.borderRightWidth = 1.0f;
    24.             divider.style.borderLeftColor = divider.style.borderTopColor = new Color(0.122f, 0.122f, 0.122f);
    25.             divider.style.borderRightColor = divider.style.borderBottomColor = new Color(0.192f, 0.192f, 0.192f);
    26.             divider.style.height = height;
    27.             return divider;
    28.         }
    The when I need it, I just call it like:

    Code (CSharp):
    1.  
    2.             Label guid = new Label();
    3.             guid.style.backgroundColor = new Color(0.1f, 0f, 0f);
    4.             guid.style.color = new Color(0.82f, 0.82f, 0.82f);
    5.             guid.text = $"GUID: {node.guid}";
    6.             guid.style.fontSize = 10;
    7.             rootElement.Add(guid);
    8.  
    9.  
    10.             rootElement.Add(VisualElementExtensions.SectionDivider());
    11.  
    12.  
    13.             Vector2Field position = new Vector2Field();
    14.             position.label = "Position:";
    15.             position.value = node.position;
    16.             position.RegisterCallback<ChangeEvent<Vector2>>(
    17.                 e =>
    18.                 {
    19.                     node.position = e.newValue;
    20.                     EditorUtility.SetDirty(node);
    21.                 });
    22.             rootElement.Add(position);
     
  45. DREBOTgamestudio

    DREBOTgamestudio

    Joined:
    Jan 30, 2018
    Posts:
    66
    Hello. We really want to be able to copy VisualElements without uxml.
     
  46. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    Does the functional approach, described just above your post, not work in your use case?

    Again, VisualElements are not something that can be serialized, so they can't be cloned. They are regular old C# objects, with pointers to other objects and various internal states. Changing these assumptions now would break a lot of user code at this point.
     
  47. Ofx360

    Ofx360

    Joined:
    Apr 30, 2013
    Posts:
    155
    So i have a ref to the UIDocument, is there anyway to get a portion of the uxml (or whatever) from there? I assumed I'd be able to target a portion of the VisualTreeAsset, keep it as a reference, then generate VisualElements of the target and its children.
     
  48. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    We currently don't have a way for you to clone just a part of VisualTreeAsset, so even if you had a reference to a specific VisualElementAsset inside the VisualTreeAsset, you wouldn't be able to clone it. You'd still need to clone the entire document and discard the rest.

    Keeping all individually cloned/instanced UI parts in separate UXML assets is the recommended way to re-use UI, instead of trying to have one large UXML asset where you clone different subtrees from.
     
    BackgroundMover likes this.