Search Unity

Resolved Load StyleSheet from String

Discussion in 'UI Toolkit' started by achimmihca, Aug 25, 2022.

  1. achimmihca

    achimmihca

    Joined:
    Feb 13, 2016
    Posts:
    283
    I am interested in user defined themes.

    What I want is to load a StyleSheet (USS) from a string.
    Note that this has been asked before: https://forum.unity.com/threads/generate-visualelementasset-stylesheet-from-a-string.851800/

    I understand that Unity needs to keep the API private as long as the implementation is unstable.
    However, my suggestion is to provide only a single method that can create a StyleSheet object from a string. This way, you can keep the StyleSheet internals / implementation non-public and change it as you please without introducing breaking changes.
    Only the single method that creates a StyleSheet from a string needs to be public.

    Do you think that this would be possible?
    If it is not possible, can you share which processing impose the limitation that StyleSheets need to be present at compile time?

    I can imagine that loading USS is easier than loading UXML at runtime. Is this true or are both tasks equally complex?

    ---
    In a first version, I could live with limited support of "load StyleSheet at runtime" that only applies new variable values.

    Example (USS):
    Code (CSharp):
    1. :root {
    2.   --primary-color: red;
    3.   --button-height: 30px;
    4. }
    I want to load such a string at runtime to overwrite existing values in my StyleSheet.
    It would allow for simple user defined themes.

    Loading StyleSheets at runtime might also be useful during development, e.g. when external designers are hired to do styling. External designers could test their StyleSheet on a built app, without installing Unity.
     
  2. uDamian

    uDamian

    Unity Technologies

    Joined:
    Dec 11, 2017
    Posts:
    1,231
    We've always wanted to ship this capability at some point, but it's just not made it to the top of the queue yet. It is slowly bubbling up and this thread definitely helps.

    That said, there are some obstacles we'd need to overcome to make this possible. It's not just a matter of making an API public. The USS importer makes heavy use of the AssetDatabase to resolve asset paths. It also uses a third-party library to parse the CSS. To make the importer runtime-ready, we'd need to come up with other ways to resolve asset paths, and we'd need to make sure the third-party CSS parsing library we use is shippable as a runtime library. Neither is impossible but it explains why we haven't prioritized it yet.

    There is still the workaround of using Asse bundles and addressables, though I understand this does make it difficult to make it work for user defined themes and modding.
     
    achimmihca likes this.
  3. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    Okay I managed to make this work by basically redoing StyleSheetImporterImpl and ignoring things like url() and resource() functions. A good solid 2 days of work. My team really needed OneJS to support runtime CSS strings (for better modding experience). So I guess I was motivated enough.



    See https://onejs.com/docs/runtimeuss for a little more info.
     
    Last edited: Apr 17, 2023
    achimmihca likes this.
  4. achimmihca

    achimmihca

    Joined:
    Feb 13, 2016
    Posts:
    283
    Very cool!
    Is it possible to overwrite variables this way (see above for an example)?

    > StyleSheetImporterImpl and ignoring things like url() and resource() functions

    Reading this, I was tempted to try it on my own. But I am unsure about licenses.
    Because at the moment, my IDE decompiles the C# class. And I doubt that I am allowed to include such a decompiled source (even if modified) in a project under MIT license (correct me if I am wrong).

    Thus, I think I have to wait for official support from Unity.
    Maybe Unity could decouple the resource features from the StyleSheetImporter in a similar way (maybe only as experimental feature) to provide one slightly limited implementation for runtime?
     
  5. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    Yup, works as you'd expect.

    I'm sure Unity has enough Good Will to be okay with this.

    In the scope of what I did, it'll be much easier for them to do since they don't need to worry about reflection and internal classes. But I think they may be aiming for a bigger refactor in general to bring StyleSheet parsing to runtime.
     
  6. fschaar

    fschaar

    Joined:
    Jul 3, 2012
    Posts:
    14
    Did you apply the importet uss to the stylesheet or did you just mod the style property of the visual element?
    From our side there was no way to fill a stylesheet by code. How did you get around this?
     
  7. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    The former (turning uss strings into UI Toolkit Stylesheets at runtime). This is awhile ago for me now, but I think I basically re-implemented their
    StyleSheetImporterImpl
    to be usable at runtime, re-using what I could and stripping away the Editor-only stuff.
     
  8. SimonHesjevik

    SimonHesjevik

    Joined:
    Aug 12, 2017
    Posts:
    1
    Has there been an update on this? I was thinking of making a component like system where you write css inline in a string that gets compiled when the component is loaded.
     
  9. achimmihca

    achimmihca

    Joined:
    Feb 13, 2016
    Posts:
    283
    > Has there been an update on this?

    Not from Unity.
    But as mentioned above, I am able to load style sheets from string using OneJs:

    Code (CSharp):
    1. public static StyleSheet CreateStyleSheet(string styleSheetContent)
    2.     {
    3.         StyleSheet styleSheet = new();
    4.         new OneJS.CustomStyleSheets.CustomStyleSheetImporterImpl().BuildStyleSheet(styleSheet, styleSheetContent);
    5.         return styleSheet;
    6.     }
    Only a fraction of OneJs is needed for this.

    However, the rest of OneJs is also pretty awesome.
    For example, it can also load the UI structure at runtime.
    This makes it perfect for modding games.

    > I was thinking of making a component like system

    OneJs implements components like React does, which is not bad.
     
    Singtaa likes this.