### History # History of Masonry and Xilem These crates evolved from various experiments in the Linebender community (Druid, masonry, idiopath, lasagna, crochet, etc) This document describes the high-level architecture of Xilem. If you want to familiarize yourself with the code base, you are just in the right place! An exploration of the ideas implemented in Xilem can be found in the following blog posts: - [Announcing Masonry 0.1, and my vision for Rust UI (2023)](https://poignardazur.github.io/2023/02/02/masonry-01-and-my-vision-for-rust-ui/) - [Xilem: an architecture for UI in Rust (2022)](https://raphlinus.github.io/rust/gui/2022/05/07/ui-architecture.html) - [Advice for the next dozen Rust GUIs (2022)](https://raphlinus.github.io/rust/gui/2022/07/15/next-dozen-guis.html) And videos: - [High Performance Rust UI (2022)](https://www.youtube.com/watch?v=zVUTZlNCb8U) - [Ergonomic APIs for hard problems (2022)](https://www.youtube.com/watch?v=Phk0C-kLlho&t=2706s) Additional content can be found in: - [Data Oriented GUI (2018)](https://www.youtube.com/watch?v=4YTfxresvS8) - [A Journey through incremental computation (2020)](https://www.youtube.com/watch?v=DSuX-LIAU-I) - [Raph Levien on UI Frameworks (2021)](https://www.youtube.com/watch?v=PwuwG2-0n3I) - [Xilem Vector Graphics (2023)](https://www.youtube.com/watch?v=XjbVnwBtVEk) - [So you want to write a GUI framework (2021)](https://www.cmyr.net/blog/gui-framework-ingredients.html) - [Rust GUI Infrastructure (2021)](https://www.cmyr.net/blog/rust-gui-infra.html) - [Towards a unified theory of reactive UI (2019)](https://raphlinus.github.io/ui/druid/2019/11/22/reactive-ui.html) - [Towards principled reactive UI (2020)](https://raphlinus.github.io/rust/druid/2020/09/25/principled-reactive-ui.html) --- ### Xilem Tutorial # Xilem tutorial ## Overall program flow > **Warning:** > > This README is a bit out of date. To understand more of what's going on, please read the blog post, [Xilem: an architecture for UI in Rust]. Like Elm, the app logic contains *centralized state.* On each cycle (meaning, roughly, on each high-level UI interaction such as a button click), the framework calls a closure, giving it mutable access to the app state, and the return value is a *view tree.* This view tree is fairly short-lived; it is used to render the UI, possibly dispatch some events, and be used as a reference for *diffing* by the next cycle, at which point it is dropped. We'll use the standard counter example. Here the state is a single integer, and the view tree is a column containing two buttons. ```rust fn app_logic(data: &mut u32) -> impl View { Column::new(( Button::new(format!("count: {}", data), |data| *data += 1), Button::new("reset", |data| *data = 0), )) } ``` These are all just vanilla data structures. The next step is diffing or reconciling against a previous version, now a standard technique. The result is an *element tree.* Each node type in the view tree has a corresponding element as an associated type. The `build` method on a view node creates the element, and the `rebuild` method diffs against the previous version (for example, if the string changes) and updates the element. There's also an associated state tree, not actually needed in this simple example, but would be used for memoization. The closures are the interesting part. When they're run, they take a mutable reference to the app data. ## Components A major goal is to support React-like components, where modules that build UI for some fragment of the overall app state are composed together. ```rust struct AppData { count: u32, } fn count_button(count: &mut u32) -> impl View { Button::new(format!("count: {}", count), |data| *data += 1) } fn app_logic(data: &mut AppData) -> impl View { lens(count_button, data, |data| &mut data.count) } ``` This `lens` node should be quite familiar to existing Druid users, and is also very similar to the [Html.map] node in Elm. Note that in this case the data presented to the child component to render, and the mutable app state available in callbacks is the same, but that is not necessarily the case. ## Memoization In the simplest case, the app builds the entire view tree, which is diffed against the previous tree, only to find that most of it hasn't changed. When a subtree is a pure function of some data, as is the case for the button above, it makes sense to *memoize.* The data is compared to the previous version, and only when it's changed is the view tree build. The signature of the memoize node is nearly identical to [Html.lazy] in Elm: ```rust fn app_logic(data: &mut AppData) -> impl View { Memoize::new(data.count, |count| { Button::new(format!("count: {}", count), |data: &mut AppData| { data.count += 1 }) }), } ``` The current code uses a `PartialEq` bound, but in practice I think it might be much more useful to use pointer equality on `Rc` and `Arc`. I anticipate it will also be possible to do dirty tracking manually - the app logic can set a dirty flag when a subtree needs re-rendering. ## Optional type erasure By default, view nodes are strongly typed. The type of a container includes the types of its children (through the `ViewTuple` trait), so for a large tree the type can become quite large. In addition, such types don't make for easy dynamic reconfiguration of the UI. SwiftUI has exactly this issue, and provides [AnyView] as the solution. Ours is more or less identical. The type erasure of View nodes is not an easy trick, as the trait has two associated types and the `rebuild` method takes the previous view as a `&Self` typed parameter. Nonetheless, it is possible. (As far as I know, Olivier Faure was the first to demonstrate this technique, in [Panoramix], but I'm happy to be further enlightened) --- ### ARCHITECTURE # ARCHITECTURE This repository holds the source code for the Xilem project and the Masonry project, including their sub-crates. - Xilem is a family of high-level GUI frameworks. Xilem apps are written with idiomatic Rust code, with little to no reliance on macros and DSLs. - **`xilem_core`** includes the traits that define Xilem. - **`xilem_masonry`** is the natively compiled framework, built on Masonry. - **`xilem`** is a batteries-included wrapper for `xilem_masonry` using `winit` for platform support. - **`xilem_web`** is the web framework, built on the DOM. - Masonry is a foundational framework for building high-level Rust GUI libraries. - **`masonry_core`** includes the base GUI engine. - **`masonry_testing`** includes a harness, helper macros and functions, etc, for testing apps built with Masonry. - **`masonry`** includes a baseline set of widgets and properties, a default theme, unit tests for widgets, and unit tests for `masonry_core`. - **`masonry_winit`** is the winit backend. See [xilem/ARCHITECTURE.md](./xilem/ARCHITECTURE.md) and [masonry/ARCHITECTURE.md](./masonry/ARCHITECTURE.md) for more details on each project. This repo also holds `tree_arena`, a crate which implements a hierarchical container, which has some properties of a tree (given a mutable reference to a node, you can get disjoint references to its value and children), while allowing `O(1)` (in unsafe mode) access to any element. ``` /* Detailed source-code truncated for AI context efficiency. */ ``` --- ### README
# Xilem **An experimental Rust architecture for reactive UI** [](https://xi.zulipchat.com/#narrow/stream/354396-xilem) [](https://github.com/linebender/xilem/actions) [](https://deps.rs/repo/github/linebender/xilem) [](#license) [](https://docs.rs/xilem)
Xilem and Masonry provide an experimental high-level architecture for writing GUI apps in Rust. **Masonry** is a foundational crate for building natively compiled GUIs in Rust. It provides a retained widget tree and runs event handling and update passes on it. **Xilem** is a high-level reactive framework inspired by React, SwiftUI and Elm. It lets users create a lightweight view tree, and changes the rendered app based on changes to the tree. It has a web backend and a Masonry backend. [`masonry/`](masonry/) and [`xilem/`](xilem/) are the respective entry points of these projects for new users. See `ARCHITECTURE.md` for details about the repository structure. Xilem and Masonry are built on top of: - **winit** for window creation. - **Vello and wgpu** for 2D graphics. - **Parley and Fontique** for [the text stack](https://github.com/linebender/parley#the-Parley-text-stack). - **AccessKit** for plugging into accessibility APIs. **Note for new users:** If you're not sure what to use between Xilem and Masonry, you probably want Xilem. In general, if you're trying to make an app with minimum hassle, you probably want Xilem. Xilem is a UI framework, whereas Masonry is a toolkit for building UI frameworks (including Xilem). ## Screenshots
*From https://github.com/StefanSalewski/xilem-chess/*
*The `calc_masonry` example.*
*The `to_do_mvc` example.*
## Getting started After cloning this repository, you can try running the examples, such as the `to_do_mvc` example shown above: ```sh cargo run --example to_do_mvc ``` To add Xilem as a dependency to your project, run ```sh cargo add xilem ``` ## Prerequisites ### Linux and BSD You need to have installed `pkg-config`, `clang`, and the development packages of `wayland`, `libxkbcommon`, `libxcb`, and `vulkan-loader`. Most distributions have `pkg-config` installed by default. To install the remaining packages on Fedora, run: ```sh sudo dnf install clang wayland-devel libxkbcommon-x11-devel libxcb-devel vulkan-loader-devel ``` To install the remaining packages on Debian or Ubuntu, run: ```sh sudo apt-get install clang libwayland-dev libxkbcommon-x11-dev libvulkan-dev ``` There's a Nix flake in `docs/` which may be used for developing on NixOS: > [!NOTE] > > This flake is provided as a starting point, and we do not routinely validate its correctness. > We do not require contributors to ensure that this accurately reflects the build requirements, as we expect most contributors (and indeed many maintainers) will not be using NixOS. > If it is out of date, please let us know by opening an issue or PR. ```sh # For all crates within this repo nix develop ./docs # For a specific crate nix develop ./docs#xilem nix develop ./docs#masonry nix develop ./docs#xilem_web ``` ## Recommended Cargo Config The Xilem repository includes a lot of projects and examples, most of them pulling a lot of dependencies. If you contribute to Xilem on Linux or macOS, we recommend using [`split-debuginfo`](https://doc.rust-lang.org/cargo/reference/profiles.html#split-debuginfo) in your [`.cargo/config.toml`](https://doc.rust-lang.org/cargo/reference/config.html#hierarchical-structure) file to reduce the size of the `target/` folder: ```toml [profile.dev] # One debuginfo file per dependency, to reduce file size of tests/examples. # Note that this value is not supported on Windows. # See https://doc.rust-lang.org/cargo/reference/profiles.html#split-debuginfo split-debuginfo="unpacked" ``` ## Minimum supported Rust Version (MSRV) This version of Xilem has been verified to compile with **Rust 1.92** and later. Future versions of Xilem might increase the Rust version requirement. It will not be treated as a breaking change and as such can even happen with small patch releases. ## Community Discussion of Xilem development happens in the [Linebender Zulip](https://xi.zulipchat.com/), specifically the [#xilem channel](https://xi.zulipchat.com/#narrow/stream/354396-xilem). All public content can be read without logging in. Contributions are welcome by pull request. The [Rust code of conduct] applies. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache 2.0 license, shall be licensed as noted in the [License](#license) section, without any additional terms or conditions. ## License Licensed under the Apache License, Version 2.0 ([LICENSE](LICENSE) or ) Some files used for examples are under different licenses: - The font file (`RobotoFlex-Subset.ttf`) in `xilem/resources/fonts/roboto_flex/` is licensed solely as documented in that folder (and is not licensed under the Apache License, Version 2.0). - The data file (`status.csv`) in `xilem/resources/data/http_cats_status/` is licensed solely as documented in that folder (and is not licensed under the Apache License, Version 2.0). - The data file (`emoji.csv`) in `xilem/resources/data/emoji_names/` is licensed solely as documented in that folder (and is not licensed under the Apache License, Version 2.0). [Rust code of conduct]: https://www.rust-lang.org/policies/code-of-conduct ---