Search Unity

Is it safe to get a StyleSheet from the default reference in the EditorWindow?

Discussion in 'UI Toolkit' started by Xarbrough, May 11, 2019.

  1. Xarbrough

    Xarbrough

    Joined:
    Dec 11, 2014
    Posts:
    1,188
    I'd like to avoid loading files from hardcoded paths, so I thought it might be possible to load a style sheet by setting a default reference in the inspector of my custom editor window script.

    EditorWindowDefaultReference.PNG


    Code (CSharp):
    1. public class MyEditorWindow : EditorWindow
    2. {
    3.     // This reference can be applied via the inspector
    4.     // of the script as a "default reference".
    5.     [SerializeField]
    6.     private StyleSheet styleSheet;
    7.  
    8.     private void OnEnable()
    9.     {
    10.         // Instead of loading by path, always use the default reference.
    11.         if (styleSheet != null)
    12.             base.rootVisualElement.styleSheets.Add(styleSheet);
    13.     }
    14. }
    In my first tests, this worked great. I can open my window, the default reference exists and I can add the stylesheet. I can also make changes to the sheet and they are instantly reflected in the window.

    However, sometimes, I get a MissingReferenceException saying that the StyleSheet ScriptableObject was destroyed (the C++ representation). My C# "styleSheet" member is not null, but somewhere a style sheet object apparently was deleted by Unity. The stack trace pointed deep into the UIElements code, no lines from my own script, I believe:

    MissingReferenceException: The object of type 'StyleSheet' has been destroyed but you are still trying to access it.
    Your script should either check if it is null or you should not destroy the object.
    UnityEngine.Object.get_name () (at C:/buildslave/unity/build/Runtime/Export/Scripting/UnityEngineObject.bindings.cs:189)
    UnityEngine.UIElements.StyleSheets.StyleSheetCache.GetPropertyID (UnityEngine.UIElements.StyleSheet sheet, UnityEngine.UIElements.StyleRule rule, System.Int32 index) (at C:/buildslave/unity/build/Modules/UIElements/StyleSheets/StyleSheetCache.cs:333)
    UnityEngine.UIElements.StyleSheets.StyleSheetCache.GetPropertyIDs (UnityEngine.UIElements.StyleSheet sheet, System.Int32 ruleIndex) (at C:/buildslave/unity/build/Modules/UIElements/StyleSheets/StyleSheetCache.cs:253)
    UnityEngine.UIElements.VisualTreeStyleUpdaterTraversal.ProcessMatchedRules (UnityEngine.UIElements.VisualElement element, System.Collections.Generic.List`1[T] matchingSelectors) (at C:/buildslave/unity/build/Modules/UIElements/VisualTreeStyleUpdater.cs:313)
    UnityEngine.UIElements.VisualTreeStyleUpdaterTraversal.TraverseRecursive (UnityEngine.UIElements.VisualElement element, System.Int32 depth) (at C:/buildslave/unity/build/Modules/UIElements/VisualTreeStyleUpdater.cs:248)
    UnityEngine.UIElements.StyleSheets.HierarchyTraversal.Recurse (UnityEngine.UIElements.VisualElement element, System.Int32 depth) (at C:/buildslave/unity/build/Modules/UIElements/HierarchyTraversal.cs:24)
    UnityEngine.UIElements.VisualTreeStyleUpdaterTraversal.TraverseRecursive (UnityEngine.UIElements.VisualElement element, System.Int32 depth) (at C:/buildslave/unity/build/Modules/UIElements/VisualTreeStyleUpdater.cs:271)
    UnityEngine.UIElements.StyleSheets.HierarchyTraversal.Recurse (UnityEngine.UIElements.VisualElement element, System.Int32 depth) (at C:/buildslave/unity/build/Modules/UIElements/HierarchyTraversal.cs:24)
    UnityEngine.UIElements.VisualTreeStyleUpdaterTraversal.TraverseRecursive (UnityEngine.UIElements.VisualElement element, System.Int32 depth) (at C:/buildslave/unity/build/Modules/UIElements/VisualTreeStyleUpdater.cs:271)
    UnityEngine.UIElements.StyleSheets.HierarchyTraversal.Traverse (UnityEngine.UIElements.VisualElement element) (at C:/buildslave/unity/build/Modules/UIElements/HierarchyTraversal.cs:11)
    UnityEngine.UIElements.VisualTreeStyleUpdater.ApplyStyles () (at C:/buildslave/unity/build/Modules/UIElements/VisualTreeStyleUpdater.cs:123)
    UnityEngine.UIElements.VisualTreeStyleUpdater.Update () (at C:/buildslave/unity/build/Modules/UIElements/VisualTreeStyleUpdater.cs:106)
    UnityEngine.UIElements.VisualTreeUpdater.UpdateVisualTree () (at C:/buildslave/unity/build/Modules/UIElements/VisualTreeUpdater.cs:70)
    UnityEngine.UIElements.Panel.Repaint (UnityEngine.Event e) (at C:/buildslave/unity/build/Modules/UIElements/Panel.cs:589)
    UnityEngine.UIElements.UIElementsUtility.DoDispatch (UnityEngine.UIElements.BaseVisualElementPanel panel) (at C:/buildslave/unity/build/Modules/UIElements/UIElementsUtility.cs:240)
    UnityEngine.UIElements.UIElementsUtility.ProcessEvent (System.Int32 instanceID, System.IntPtr nativeEventPtr) (at C:/buildslave/unity/build/Modules/UIElements/UIElementsUtility.cs:78)
    UnityEngine.GUIUtility.ProcessEvent (System.Int32 instanceID, System.IntPtr nativeEventPtr) (at C:/buildslave/unity/build/Modules/IMGUI/GUIUtility.cs:179)

    Do I have to load my sheet from path in OnEnable because of limitations with this approach or am I seeing exceptions because of a more general issue with style sheets sometimes being destroyed?
     
  2. patrickf

    patrickf

    Unity Technologies

    Joined:
    Oct 24, 2016
    Posts:
    57
    Hi! This looks like a bug. Could you please report it with details on how to reproduce it? Thanks!
     
    Xarbrough likes this.
  3. Leslie-Young

    Leslie-Young

    Joined:
    Dec 24, 2008
    Posts:
    1,148
    How about using
    Resources.Load<StyleSheet>("path/style");
    . Then place styles in a path like
    Assets/Path/Editor/Resources/path/style.uss


    The part up to Editor/Resources can be anything and that would normally be what a user would want to change if you were making a tool to be used in other projects.

    [edit] However, thinking about it now. I did also get an exception related to the style sheet now and then while making changes to the code and/or style.
     
  4. Xarbrough

    Xarbrough

    Joined:
    Dec 11, 2014
    Posts:
    1,188
    Yes, I will do that tonight!

    I've observed my tool for a couple of days now and found that the posted exception was also thrown occasionally when I loaded the style sheet by path. So, I assume the issue is not related to the approach of using default references.

    That would work, but I generally found this more difficult to work with if there is only a single file to reference. In our team we like to split our content into functional components and not by category or type. This means I would be adding multiple (many) Resources folders to the project for single files etc. The default references approach actually works perfectly for this type of thing and after looking into the issue I think the exception is not even related to the use of the default reference.
     
  5. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    There is a currently a bug where changing a USS file AND some C# (that triggers a domain reload) at the same time will cause the StyleSheet reference to go null at some point and you have to close and re-open your EditorWindow.
     
    Xarbrough likes this.
  6. VOTRUBEC

    VOTRUBEC

    Joined:
    Dec 17, 2014
    Posts:
    106
    Hi Damian, I suspect I'm running into that exact issue. I've followed the execution path and came to this:

    Code (CSharp):
    1. // UnityEngine.UIElements.VisualElement
    2. internal void AddStyleSheetPath(string sheetPath)
    3. {
    4.     StyleSheet styleSheet = Panel.LoadResource(sheetPath, typeof(StyleSheet), scaledPixelsPerPoint) as StyleSheet;
    5.     if (styleSheet == null)
    6.     {
    7.         Debug.LogWarning($"Style sheet not found for path \"{sheetPath}\"");
    8.     }
    9.     else
    10.     {
    11.         styleSheets.Add(styleSheet);
    12.     }
    13. }
    Obviously, I'm getting the LogWarning for all the elements in the graph when the domain is reloaded. Is there somewhere I can follow the bug and the progress? Is there a quick workaround at the moment, such as trying to reload the graph?
     
  7. antoine-unity

    antoine-unity

    Unity Technologies

    Joined:
    Sep 10, 2015
    Posts:
    780
    Hi, which version of Unity are you currently using ? IIRC this bug has been fixed
     
  8. VOTRUBEC

    VOTRUBEC

    Joined:
    Dec 17, 2014
    Posts:
    106
    Hi Antoine,

    I'm using 2019.3.3f1.

    I'm in the process now of trying to load the USS myself, OR, whether I could strip the USS code, and insert it into my own USS file.

    I'm also in the process of trying to load the resource manually, but I don't think this is how loading a resource works:
    var nodess = Resources.Load<StyleSheet> ( "StyleSheets/GraphView/Node" ); // with and without the .uss.

    I'm running in to a lot of "internal" methods and properties which is really slowing me down. I'm either using reflection in some cases, or rewriting methods.

    But I shouldn't complain. I mean, the names space does actually have the word "experimental" in it! haha :)
     
    Last edited: Mar 5, 2020
  9. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    There shouldn't be anything "Experimental" in the 2019.3 UIElements APIs that you would need for the above task. I'm curious which APIs you're referring to.

    The bug I mentioned about USS and UXML not loading properly if both have been modified externally at the same time has long since been fixed. It should be fixed in 2019.3.

    If you're writing for the Editor, the above example should work nicely as it avoids paths altogether and the bugs that Xarbrough encountered should be fixed by now (that was almost a year ago). You should ideally not use Resources for editor UI and just
    AssetDatabase.LoadAssetAtPath()
    .