MIT License

项目介绍

audulus/rui 是一个托管在 GitHub 上的开源项目,主要使用 Rust 编写,采用 MIT License 许可证,目前获得 2.0k 个 Star、46 次 Fork。

仓库全名
audulus/rui
开源许可证
MIT License
Star 数
2.0k
Fork 数
46
未关闭 Issue
14

README 摘要

rui Experimental Rust UI library, inspired by SwiftUI. Early days, but some stuff already works. rui will be used for a future version of Audulus rui is GPU rendered and updates reactively (when your state changes). The focus of rui is to have the best ergonomics, and use the simplest possible implementation. As such, there is no retained view tree (DOM) or view diffing. Everything is re-rendered when state changes,…

完整内容见 GitHub 原始 README 或本页 。

常见问题

rui 用什么语言开发?

主要使用 Rust 编写,采用 MIT License 许可证,目前有 2.0k 个 Star。

rui 可以免费商用吗?

仓库采用 MIT License 许可证,可免费使用与修改,商业使用请遵守许可证条款(例如保留版权声明与许可声明)。

如何获取 rui 的源码?

可以直接克隆仓库:git clone https://github.com/audulus/rui.git,默认分支为 main;安装与运行步骤见仓库 README。

rui 最近还在维护吗?

仓库最近一次更新为 5月22日,当前有 14 个未关闭 Issue;Star 数为 2.0k,可作为社区活跃度的参考。

关于

GitHub 原始描述(英文)

Declarative Rust UI library

描述取自 GitHub 官方 API,保持仓库作者书写的原文。

简要分析

热度分
2158
价值分
1241
活跃状态
活跃

语言占比

Python
Rust
Shell

README

logo

rui

build status dependency status

Experimental Rust UI library, inspired by SwiftUI. Early days, but some stuff already works. rui will be used for a future version of Audulus

rui is GPU rendered and updates reactively (when your state changes). The focus of rui is to have the best ergonomics, and use the simplest possible implementation. As such, there is no retained view tree (DOM) or view diffing. Everything is re-rendered when state changes, under the assumption that we can do that quickly.

discord server

Examples

obligatory Counter:

cargo run --example counter
use rui::*;

fn main() {
    state(
        || 1,
        |count, cx| {
            vstack((
                cx[count].padding(Auto),
                button("increment", move |cx| {
                    cx[count] += 1;
                })
                .padding(Auto),
            ))
        },
    )
    .run()
}
counter screenshot

some shapes:

cargo run --example shapes
use rui::*;

fn main() {
    vstack((
        circle()
            .color(RED_HIGHLIGHT)
            .padding(Auto),
        rectangle()
            .corner_radius(5.0)
            .color(AZURE_HIGHLIGHT)
            .padding(Auto)
    ))
    .run()
}
shapes screenshot

canvas for gpu drawing:

cargo run --example canvas
use rui::*;

fn main() {
    canvas(|_, rect, vger| {
        vger.translate(rect.center() - LocalPoint::zero());

        let paint = vger.linear_gradient(
            [-100.0, -100.0],
            [100.0, 100.0],
            AZURE_HIGHLIGHT,
            RED_HIGHLIGHT,
            0.0,
        );

        let radius = 100.0;
        vger.fill_circle(LocalPoint::zero(), radius, paint);
    })
    .run()
}
canvas screenshot

slider with map:

cargo run --example slider
use rui::*;

#[derive(Default)]
struct MyState {
    value: f32,
}

/// A slider with a value.
fn my_slider(s: impl Binding<f32>) -> impl View {
    with_ref(s, move |v| {
        vstack((
            v.to_string().font_size(10).padding(Auto),
            hslider(s).thumb_color(RED_HIGHLIGHT).padding(Auto),
        ))
    })
}

fn main() {
    state(MyState::default, |state, cx| 
        map(
            cx[state].value,
            move |v, cx| cx[state].value = v,
            |s, _| my_slider(s),
        ),
    )
    .run()
}
slider screenshot

calculator:

cd examples/calculator && cargo run
calculator screenshot

synth:

cd examples/synth && cargo run
synth screenshot

widget gallery:

cargo run --example gallery
widgets gallery screenshot

Goals

  • Encode UI in types to ensure stable identity.
  • Optimize to reduce redraw.
  • Use vger-rs for rendering.
  • Minimal boilerplate.
  • Good looking.
  • No unsafe.
  • No use of RefCell (leads to potential runtime errors).
  • Accessibility for assistive technologies.

Optional Features

Why and how?

In the long term, I’d like to move Audulus over to Rust. After looking at other available UI options, it seemed best to implement something resembling the existing immediate mode UI system I already have working in Audulus, but better.

I had been enjoying the ergonomics of SwiftUI, but SwiftUI simply can’t handle big node graphs very well (we have tried and had to fall back to manual layout and render with Canvas, so we couldn’t put custom Views within each node). What you find with SwiftUI (particularly when profiling) is that there’s a lot of machinery dealing with the caching aspects of things. It’s opaque, scary (crashes on occasion, parts are implemented in C++ not Swift!), and can be rather slow. Often, it seems to be caching things thare are trivial to recompute in the first place.

Not so long ago, before programmable shaders, it was necessary to cache parts of a UI in textures (CoreAnimation for example does this) to get good performance. Now we have extremely fast GPUs and such caching is not necessary to achieve good performance. In fact if enough is animating, lots of texture caching can hinder performance, since the caches need to be updated so often. Plus, the textures consume a fair amount of memory, and when you have an unbounded node-graph like Audulus, that memory usage would be unbounded. And what resolution do you pick for those textures?

So rui starts from the assumption that 2D UI graphics (not general vector graphics!) are a trivial workload for a GPU. If you consider how advanced games are now, doing realtime global illumination and such, this seems intuitively correct, but Audulus more-or-less proves it. So that means we can do away with the texture caching, and we really might not even need damage regions either. I’m also skeptical of the need for parallel encoding or caching parts of the scene for 2D UI graphics, since, again, it’s just a trivial GPU workload.

Layout, on the other hand, can’t easily be offloaded to GPU free-performance land. It’s necessary to cache layout information and try not to recompute it all the time. So rui caches layout and only recomputes it when the state changes (unlike a typical immediate mode UI which computes layout on the fly and is constrained to very simple layouts). For Audulus, this isn’t quite enough, since some view-local state will be changing all the time as things are animating (Audulus solves this by only recomputing layout when the central document state changes). Perhaps this is where proponents of DOM-ish things (some other OOP-ish tree of widgets) would jump in and make their case, but I’m skeptical that’s really necessary. Think of what actually needs to be (re)computed: a layout box for each (ephemeral) View. Does this really require a separate tree of objects? Time will tell!

Status

  • ✅ basic shapes: circle, rounded rectangle
  • ✅ basic gestures: tap, drag
  • ✅ hstack/vstack
  • ✅ text
  • ✅ padding
  • ✅ offsets
  • ✅ state
  • ✅ zstack
  • ✅ canvas (GPU vector graphics with vger)
  • ✅ bindings
  • ✅ list
  • ✅ sliders
  • ✅ knobs
  • ✅ editable text (still a bit rough)
  • ✅ any_view (view type erasure)
  • ✅ layout feedback
  • ✅ animation
  • ✅ UI unit testing

References

Towards principled reactive UI

Towards a unified theory of reactive UI

Flutter’s Rendering Pipeline

Static Types in SwiftUI

How Layout Works in SwiftUI

Xilem: an architecture for UI in Rust

评论

暂无评论