Search Unity

Introducing Yew, another way of creating UI in Unity

Discussion in 'UI Toolkit' started by Bennidhamma2, Mar 3, 2021.

  1. Bennidhamma2

    Bennidhamma2

    Joined:
    Jan 5, 2019
    Posts:
    23
    Hello y'all,

    I spent the week putting together a little React-like library that wraps UI Toolkit I'm calling "Yew". Yew lets me express my UI the way I'm used to with the web and react. In addition to React, it's also inspired by some other projects, a big one being the new "MVU" style of .NET MAUI.

    Yew code looks like this:

    Code (CSharp):
    1. public class CounterApp : View
    2. {
    3.     public class Component : YewLib.Component
    4.     {
    5.         public override View Render()
    6.         {
    7.             var state = UseState(0);
    8.             return new Flex()
    9.             {
    10.                 Label($"{state.Value}"),
    11.                 Button("Increment", () => state.Value++)
    12.             };
    13.         }
    14.     }
    15. }
    And to plant it into a UI Document, from a MonoBehavior:

    Code (CSharp):
    1. var uiDoc = GetComponent<UIDocument>();
    2. Yew.Render(new CounterApp(), uiDoc.rootVisualElement);


    I have a few samples, a unityproject installer, and some documentation up on github: https://github.com/Grumpy-Raven/yew. I'd love it if folx who were interested would take a look and let me know if this feels like something that's worth pursuing further.

    all the best,

    -- ben
     
    Last edited: Mar 3, 2021
  2. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    Looks interesting. Seems like a possible alternative to IMGUI for editor.
     
  3. Bennidhamma2

    Bennidhamma2

    Joined:
    Jan 5, 2019
    Posts:
    23
    How so? I haven't used IMGUI much. I'm using this for the runtime, with UI Toolkit (VisualElements), so I get the benefit of styling with flexbox layouts and the stuff USS provides.
     
  4. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    It ties together the layout with the logic. Which is fine for things with smaller scopes, like an editor window, where ease of use is paramount. But I would be scared to use this for a large project especially if it was different layouts on different platforms (mobile vs desktop vs console vs tablet vs VR/AR, etc).

    Regardless, Yew has its place and it seems like the API is very straightforward to use.
     
  5. Bennidhamma2

    Bennidhamma2

    Joined:
    Jan 5, 2019
    Posts:
    23
    Hmm. I don't doubt I have a lot to learn about building larger applications across multiple device and form factors for Unity. But, this approach, let's call it MVU-ish, is used by millions of React developers to build very large and complex web and react native apps that are responsive to different layouts (for sure, not AR/VR though)

    On the web at least, we have a variety of tools to create responsive and adaptive layouts. Media queries being one big one, which I don't think we have in USS (yet?). I'll put some thought into how we might go about building adaptive containers. Flex layouts can be used pretty effectively sometimes to get side by side vs stacking layouts.

    also, I expect we can get a lot of mileage from dynamically loading the style sheet based on form factor. For example:

    Code (CSharp):
    1. var stylesheet = Screen.width < 400 ? "UI/mobile.uss" : "UI/desktop.uss";
    2. return new StackLayout(className: "root", style: stylesheet) ...
    That's where you really *should* be doing most of your form factor specific logic anyways. If Unity is serious about moving towards more a more web-like paradigm, the element hierarchies, be they Yew, POCO, or UXML, really should be describing more of the semantics and intent of UI, versus being tightly coupled to a specific layout. Hopefully USS is (or will soon be) rich enough to let us manage the layout with style rules. Otherwise, it seems kind of like... what's the point? Or maybe it's just not the goal to be attracting web developers, but just to borrow some design concepts from that space? Which I guess could make some kind of sense if I sat and thought about it for long enough...
     
    Last edited: Mar 4, 2021
  6. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,336
    So, by looking at it, my initial reaction was that it looked way, way overengineered. That being said, I'm not overly familiar with the whole web ecosystem in general or the React framework in particular.

    So I sat down and tried to rebuild the Todo app from scratch - ie. using just C#, uss, and some extension methods in order to be able to write this:

    Code (csharp):
    1. var textInputField = newTodo.AddTextField(label: "What do you need to do?",
    2.                                           className: "textfield");
    instead of:
    Code (csharp):
    1. var textField = new TextField("What do you need to do?");
    2. textField.AddToClassList("textfield");
    3. newTodo.Add(textField);
    That's essentially how I build UI Toolkit stuff now.


    So what I figured out is that your thing makes the code marginally easier to write, by adding a pretty large framework, and a substantial performance overhead - especially when it comes to object allocations.

    The reason why it's easier is that every time the UI needs to change due to an event, the entire View object is regenerated from scratch. Then, behind the scenes, that's turned into VisualElements through a framework that tries to automatically pool those VisualElements by figuring out if it's the same ones that are getting reused.

    The way I'd do this from scratch in C# is to have event listeners in the correct spots that adds and removes just the parts of the UI that needs to be added or removed. That's harder to get right, but it's for sure doable.


    So, pure C#:
    Add button is pressed
    New todo entry is added to the todo list
    New todo VisualElement is created, and tied to the created todo entry
    The new VisualElement is inserted at the correct spot in the hierarchy

    Yew:
    Add button is pressed
    New todo entry is added to the todo list
    Entire Yew representation is recreated from scratch
    Yew walks the VisualElement hierarchy to figure out if there are updates needed
    The new VisualElement is created as a side-effect of the above.


    The end result is that it's slightly easier to make the UI update, through a "do everything from scratch but like in an optimized way" approach. There's only so far you can optimize a bad way of doing things, though. This should probably never be used in production, especially since it absolutely pukes allocations - both due to regenerating the Yew hierarchy, and also due to the implementation being very naive about allocations.

    To put it in a different way - in order to avoid regenerating VisualElements when the UI change, it regenerates Yew elements. Presumably the assumption is that that's cheaper, and it probably is, but by how much?

    Since it's so slow, you can only really use it if you don't care about the performance or are prototyping. But if you're doing that, you might as well just rebuild the VisualElement tree from scratch instead, so I don't really see any instances where I'd want to use this.


    Also, the UseAtom stuff is the most overengineered Dictionary<string, object> I have seen in my life. The view should act on the data, not manage where and how it's stored. For any real use-case, the data would live externally (and usually come from different places from UI to UI). For a quick-and-dirty thing like the Todo App, you can just have the static field, it doesn't need to be fetched from a global storage through a string.

    Finally, the way you load stylesheets by using the AssetDatabase API doesn't work in builds. That's an Editor only API, so you either have to load the stylesheet from resources, or link it when you combine the ui document and the Yew component.
     
  7. Bennidhamma2

    Bennidhamma2

    Joined:
    Jan 5, 2019
    Posts:
    23
    Hey Baste! Thanks for taking the time to dig in :) And for sure, you may not be the target audience for a framework like this. A few clarifications:

    This is just a proof of concept. I put it together in a week, to see if I could put together a way to express UI that's more palatable to folks coming from the React world (which even though it's the most popular way to build modern web apps, is no stranger to controversy in that world). In that spirit, I've specifically avoided addressing obvious performance tasks, like freeing up objects, using a weak event manager, or anything but the most basic optimizations. I'm focusing more on expressiveness and correctness in reconciliation than I am on perf.

    "by adding a pretty large framework"

    I wouldn't call it large - it's around 11kb, including samples...

    "The reason why it's easier is that every time the UI needs to change due to an event, the entire View object is regenerated from scratch. Then, behind the scenes, that's turned into VisualElements through a framework that tries to automatically pool those VisualElements by figuring out if it's the same ones that are getting reused."

    Yew updates subtrees when those subtrees have changed. So we aren't regenerating the entire hierarchy each time from scratch. And the thinking here, which matches Reacts, is that these are cheap objects to generate. If/when we get record types, Yew would like to use those. creating and disposing small, short-lived objects isn't free, but it's a lot better than bigger, longer running objects with lots of references.

    "The way I'd do this from scratch in C# is to have event listeners in the correct spots that adds and removes just the parts of the UI that needs to be added or removed. That's harder to get right, but it's for sure doable."

    In my experience, this is what tends not to scale well as your app's UI becomes more complex. The reason why React is popular with so many millions of developers is because doing all of that precise, manual targeting of updating becomes unmanageable when you are working on production class applications, and the web industry made the decision that it's worth taking a modest hit in runtime performance to allow their teams to build richer, more maintainable, higher quality applications.

    "Yew:
    Add button is pressed
    New todo entry is added to the todo list
    Entire Yew representation is recreated from scratch
    Yew walks the VisualElement hierarchy to figure out if there are updates needed
    The new VisualElement is created as a side-effect of the above."

    This is sort of true, for this sample. However (as I point out in the yew documentation), the use of 'method components' is for lighter weight areas, and for prototyping. As UI complexity grows, those can be migrated easily to regular View/Components, which will then not need to render their child hierarchies, unless their state changes. So, say you had a list of 100 complex item components, you can provide lightweight views for them, and only when the view has changed, do you need to pay the cost to rebuild that subtree.

    "Since it's so slow, you can only really use it if you don't care about the performance or are prototyping. "

    Have you downloaded and ran the samples and benchmarked their performance? If so, thank you! and please share the numbers! Otherwise, please do before you lob baseless accusations.

    "Also, the UseAtom stuff is the most overengineered Dictionary<string, object> I have seen in my life. "

    Thanks for the additional kind words :) I won't defend the UseAtom code. That represented about 30 minutes of the week long project. I don't know if I'd use them either. it's more about just trying things out, to see if they stick at this point. FWIW, the atoms come from recoil (https://recoiljs.org/), and are considered experimental over in the web world too. I'm planning on playing with the idea a bit more, but yeah, I wouldn't use them at this point in production.

    "Finally, the way you load stylesheets by using the AssetDatabase API doesn't work in builds. That's an Editor only API, so you either have to load the stylesheet from resources, or link it when you combine the ui document and the Yew component."

    good to know. you have a link? I'll get that sorted.

    Thanks again Baste, even though it's clearly not to your taste, I appreciate the time to understand it at the level you did.

    And yes, I agree with the assessment that this is NOT production ready! It's a week old project! At this point, I'm really just curious if there's any interest in this approach of expressing UI. I know it's a popular paradigm in the web world, but I really know nothing about UI opinions in Unity. All I know is that I found the state of UI in Unity rough enough that it was worth it to me, for my own sanity, to have a go at creating some of the familiar tools and concepts I love from the web world.
     
    Last edited: Mar 5, 2021
    leutnant_kane and JoNax97 like this.