All Projects → Grumpy-Raven → yew

Grumpy-Raven / yew

Licence: MIT License
Yew is a library to help make it easier to write interactive UI with Unity's UI Toolkit. If you like React for Web/native, you just might like Yew.

Programming Languages

C#
18002 projects

Projects that are alternatives of or similar to yew

Keemun
No description or website provided.
Stars: ✭ 13 (-51.85%)
Mutual labels:  elm-architecture, mvu
dotnet
.NET Community Toolkit is a collection of helpers and APIs that work for all .NET developers and are agnostic of any specific UI platform. The toolkit is maintained and published by Microsoft, and part of the .NET Foundation.
Stars: ✭ 865 (+3103.7%)
Mutual labels:  maui
net6.0-mobile-fsharp
Mobile templates for .NET 7.0 / F# 7.0
Stars: ✭ 76 (+181.48%)
Mutual labels:  maui
elm-from-ground-up
This lab is designed to be an adjunct to learning Elm. Go from the most basic "Hello World" through data types and more advanced features in this example.
Stars: ✭ 17 (-37.04%)
Mutual labels:  elm-architecture
tea-chess
A chess-themed tutorial on writing an SPA in Bucklescript-TEA
Stars: ✭ 28 (+3.7%)
Mutual labels:  elm-architecture
EBind
🔵 .NET Data Binding we deserve: concise, fast, feature-rich
Stars: ✭ 141 (+422.22%)
Mutual labels:  maui
Fabulous.WPF
WIP - Declarative UIs for WPF with F# and MVU, using Fabulous
Stars: ✭ 24 (-11.11%)
Mutual labels:  mvu
Elmdroid
Minimalistic Android implementation of The Elm Architecture with android architecture components integration.
Stars: ✭ 25 (-7.41%)
Mutual labels:  elm-architecture
Teapot
Unidirectional Dataflow library for Android inspired by The Elm Architecture
Stars: ✭ 29 (+7.41%)
Mutual labels:  elm-architecture
elm-nested-component-communication
Example of Sibling/Nested communication in elm
Stars: ✭ 18 (-33.33%)
Mutual labels:  elm-architecture
UniTEA
Implementation of The Elm Architecture for Unity3D
Stars: ✭ 31 (+14.81%)
Mutual labels:  elm-architecture
temperjs
State management for React, made simple.
Stars: ✭ 15 (-44.44%)
Mutual labels:  recoiljs
mvvmgen
MvvmGen is a lightweight MVVM library for XAML applications. It generates your ViewModels on-the-fly for you via a Roslyn-based C# Source Generator.
Stars: ✭ 179 (+562.96%)
Mutual labels:  maui
MADE.NET
MADE.NET is a home to all of those bits of code that you know you'll reuse in another project. Making app development easier with .NET.
Stars: ✭ 75 (+177.78%)
Mutual labels:  maui
entangle
Global state management tool for react hooks inspired by RecoilJS and Jotai using proxies.
Stars: ✭ 26 (-3.7%)
Mutual labels:  recoiljs
larch
An Elm-like ClojureScript framework
Stars: ✭ 37 (+37.04%)
Mutual labels:  elm-architecture
concur-replica
Server-side VDOM UI framework for Concur
Stars: ✭ 136 (+403.7%)
Mutual labels:  elm-architecture
atomic-state
A decentralized state management library for React
Stars: ✭ 54 (+100%)
Mutual labels:  recoiljs
edliz
This 7th essential medicines list and standard treatment guidelines for the most common health conditions in Zimbabwe has been endorsed by the National Medicine & Therapeutics Policy Advisory Committee [NMTPAC]. It is the product of many years of combined efforts by hundreds of health workers at all levels of the health care system in Zimbabwe. …
Stars: ✭ 25 (-7.41%)
Mutual labels:  recoiljs
re-alm
An Elm Architecture experiment in ClojureScript
Stars: ✭ 24 (-11.11%)
Mutual labels:  elm-architecture

yew

Yew is a library to help make it easier to write interactive UI for Unity. Get it? Yew...nity?

It's inspired by things like React, Elm, Recoil, and .NET MAUI.

Rationale

There's a lot to like with the upcoming support for UI Toolkit in the Unity runtime. Coming from a web development background, the similarity between HTML/CSS & UXML/USS is just wonderful. But, I've been using libraries like React for so long that I actually don't really love hand editing UXML and digging around with selector queries to find nodes in my C# code, and the steps it seems to take to get custom components created and usable in the UI Builder tool just feel too heavy.

Coming from a world where creating a functional component is as easy as:

var OneTwoThree = ({ num }) => <div>{num}</div>

I wanted to have the flexibility and familiarity of designing layouts using classes and CSS-like rules, and the speed of iteration using component driven architectures. So, Yew.

What does it look like?

Let's start with a simple example, ye old counter:

public class CounterApp : View
{
    public class Component : YewLib.Component
    {
        public override View Render()
        {
            var state = UseState(0);
            return new Flex()
            {
                Label($"{state.Value}"),
                Button("Increment", () => state.Value++)
            };
        }
    }
}

And to plant it into a UI Document, from a MonoBehavior:

var uiDoc = GetComponent<UIDocument>();
Yew.Render(new CounterApp(), uiDoc.rootVisualElement);

That's it!

Shall we talk a bit about what's going on here?

We have a CounterApp View, and a CounterApp.Component.

Yew maintains a hierarchy of disposable view elements. I borrowed heavily from React's design here (Note: What Yew calls "views", React calls "elements") It turns out, these things are pretty dang useful for reconciliation. Yew maintains a tree of Yew Nodes where each node is responsible for: an associated VisualElement, a Yew View, and an instance of a Yew Component. Nodes also maintain a list of child nodes, and Yew uses an implementation of Longest Common Subsequence (yeah - all that dynamic programming interview study actually paid off - this is the first time I've ever had a reason to use a tabulation based dynamic programming algorithm in something that isn't just an interview problem) to reconcile children, adding, updating and inserting nodes as needed. (Note: this is different than what React does, or did at least, per this doc on react reconciliation. I couldn't find a great discussion of how modern react reconciles, if anyone has insight here, I'd love to hear it. Yew's LCS reconciler is O(m*n^2) where m is the number of nodes and n is the average number of children per node, whereas react claims to have a linear time reconciler, which I'm definitely curious about.)

Unless the View derives from YewLib.Primitive (used for directly emitting VisualElements), it will probably contain a child Component type. This is where you actually implement UI logic, work with state, render a subtree, and so on. I played around with a number of different patterns here, and for me, it's all about ease of readability and speed of expressing my ideas through code. So, while I'm not normally a big fan of convention over configuration, being able to just know that there is a nested Component type makes the code really easy to write, and you don't have to come up with two names for everything - (naming things once is bad enough!)

Render method

Components implement Render methods, just like React, which return a view hierarchy. In the counter sample, we return a Flex View (a ContainerView designed to easily align things horizontally instead of vertically), which contains a Label and a Button. Pretty easy right? When the button is clicked, the state is updated, which triggers a component update, and voila! you have an updated label.

Wait - is that a useState hook?

It is! I am a fan of react's hooks and found that putting them into Yew wasn't too difficult. C#'s syntax sugar makes them easy to use too.

You can also call UseState inside of "method components":

View ItemRenderer(int i)
{
    var state = UseState(0);
    return new StackLayout()
    {
        Label($"Counter #{i}: {state.Value}"),
        Button($"Incr #{i}", () => state.Value++),
    };
}

public override View Render() => new StackLayout {
  ItemRenderer(1),
  ItemRenderer(2),
}

"Method components" aren't quite like React's functional components, for instance, they currently aren't capable of being independently updated by Yew (they update when their parent Component updates). But they can independently store state. I'm planning on using them for laying out lighter weight items in my UI, and when they get bigger, I'll probably move them into their own full fledged View/Components.

What about global state?

I tried playing around with some of the various attempts at porting Redux over to C# and Unity (if anyone wants to give this a go, I'd love to post a sample on how to do that). I do think that when Unity supports C# 9.0, some nice redux-y stuff will be easier to do with the new record types.

I did stumble across recoiljs, and the "atom" pattern was pretty easy to layer in.

var state = UseAtom(TodoAppKey, () => new TodoState());

What this does is creates a global bit of state, which lives independently of the component. So, if this component gets cleaned up, you don't lose the state. Nice! If you want to dig in a little more into Yew atoms, have a look at the Todo App (and you can see how to 'subscribe' to the atom value in the hello world sample).

Atoms can be referenced outside of Yew component trees. This could be useful for things like animations, or integration with other parts of your game (or whatever it is you do with Unity)

If you want to get updates when an atom changes, implement YewLib.IUpdatable (just a void Update() method) and Subscribe() to the atom. (yeah, super simple and naive observer stuff, sorry not sorry).

Note also, you can use lambdas for more complex state constructors. Another note: atoms don't currently garbage collect. Let's call that a TODO shall we?

Yew Runtime and Animations

Speaking of animations, Yew now has a Runtime MonoBehavior, which if installed, allows components to request animation frames. This is what it looks like:

Here is the salient bit of the TypeWriter sample:

public override View Render()
{
    var len = UseState(0);
    IEnumerator anim()
    {
        while (len < Props.Text.Length) {
            len.Value++;
            yield return new WaitForSeconds(0.1f);
        }
    }
    UseCoroutine(anim);
    string text = Props.Text;
    if (len < text.Length)
        text = $"{text.Substring(0, len)}<alpha=#00>{text.Substring(len)}";
    return Label(text, className: "typewriter");
}

Anything Else?

A few little tidbits I think are worth pointing out.

Use with switch expressions (sample)

  View SampleChoice(Choice choice) => choice switch
  {
      Choice.Counter => new CounterApp(),
      Choice.Todo => new TodoApp(),
      Choice.HelloWorld => new HelloWorld(),
      _ => new Label("Choose a sample to learn more about yew")
  }

Render data item collections with Select and Method Components (sample)

return new StackLayout()
{
   state.Value.TodoItems
     .Where(x => !x.Completed)
     .Select(item => TodoItemView(state.Update, item))

HTML / React style attributes (sample)

We do these in constructors, rather than object initializers, so as not to conflict with list initialization. Fortunately, named optional parameters make this very pleasant to do:

return new StackLayout(className: "root", style: "UI/styles.uss")            

Oh yeah, that's how we get styles associated. Which is useful. I don't fully know my way around UI Toolkit yet, but I think Yew should inherit pretty much all of the functionality of UI Toolkit, well, except for UI Builder. But, you can edit your stylesheets and see those changes get reflected in real time, and you can use the nifty UI Toolkit Debugger tool. Editing the C# does not give a good hot reload experience, at least I haven't figured that one out very well yet.

Installation

Yew is available as a Unity package. link

Examples

These shouldn't be too hard to get running... Let me know if you get stuck.

To Dos

  • Flesh out the primitives more. (images, sliders, select boxes, scrollers ..., and expose more attributes, such as OnClick for Labels, OnEnter for textboxes, ...)
  • Performance / make sure stuff gets freed up when things unmount
  • Maybe support animations
  • What would you do? If you were yew?

License

This project is licensed under the terms of the MIT license.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].