131 lines
4.6 KiB
Markdown
131 lines
4.6 KiB
Markdown
# iris
|
|
|
|
My experimental attempt at a rust ui library (also my first ui library).
|
|
|
|
It's currently designed around using retained data structures (widgets), rather than diffing generated trees from data like xilem or iced. This is an experiment and I'm not sure if it's a good idea or not.
|
|
|
|
Examples are in `examples`, eg. `cargo run --example tabs`. Each example keeps
|
|
its widget tree in `lib.rs` and its small desktop and Android hosts in
|
|
`desktop.rs` and `android.rs`.
|
|
|
|
## Android applications
|
|
|
|
An Android application is a library because Android loads its Rust code as a
|
|
native shared library:
|
|
|
|
```toml
|
|
[lib]
|
|
crate-type = ["cdylib", "rlib"]
|
|
|
|
[dependencies]
|
|
iris = { path = "../iris" }
|
|
|
|
[package.metadata.iris.android]
|
|
application-id = "com.example.myapp"
|
|
label = "My app"
|
|
```
|
|
|
|
`#[iris::app_init]` marks the initializer called when Android creates the Iris
|
|
view. The attribute supplies its own Android target gate and generates the JNI
|
|
loader glue. An application with no state beyond the UI state receives
|
|
`AndroidUiState` directly by mutable reference:
|
|
|
|
```rust
|
|
use iris::prelude::*;
|
|
|
|
fn build<Rsc: UiRsc>(rsc: &mut Rsc, ui_state: &mut impl HasRoot<Rsc>) {
|
|
rect(PaintId::RED).set_root(rsc, ui_state);
|
|
}
|
|
|
|
#[iris::app_init]
|
|
fn create(
|
|
ui_state: &mut AndroidUiState,
|
|
rsc: &mut StdRsc<AndroidUiState>,
|
|
) {
|
|
build(rsc, ui_state);
|
|
}
|
|
```
|
|
|
|
Desktop has the corresponding initializer form:
|
|
|
|
```rust
|
|
DesktopApp::<DesktopUiState>::run_with(build);
|
|
```
|
|
|
|
Background work updates either host through the same task context. An update
|
|
wakes the UI thread; Iris schedules a frame automatically if the closure made
|
|
the retained widget tree dirty:
|
|
|
|
```rust
|
|
rsc.spawn_task(async move |mut ctx| {
|
|
let text = load_text().await;
|
|
ctx.update(move |_, rsc| label.edit(rsc).set(&text));
|
|
});
|
|
```
|
|
|
|
Applications do not need a winit event proxy or an explicit redraw request.
|
|
|
|
Applications with additional fields use their own state type. Its
|
|
`AndroidAppState::Resources` associated type can also replace `StdRsc` with a
|
|
custom resource bundle.
|
|
|
|
Install the Cargo subcommand from a checkout, then invoke it from the
|
|
application's directory:
|
|
|
|
```sh
|
|
cargo install --path /path/to/iris/cargo-iris
|
|
cargo iris apk --abi arm64-v8a
|
|
cargo iris run --abi x86_64 --device emulator-5554
|
|
```
|
|
|
|
Package examples use the same command with `--example`:
|
|
|
|
```sh
|
|
cargo run --example tabs
|
|
cargo iris apk --example tabs --abi arm64-v8a
|
|
cargo iris run --example tabs --abi x86_64 --device emulator-5554
|
|
```
|
|
|
|
`cargo iris` packages directly with `cargo-ndk`, `javac`, `d8`, `aapt2`,
|
|
`jar`, `zipalign`, and `apksigner`; it does not require Gradle. The caller
|
|
provides a JDK, Android SDK and NDK, and any emulator or physical device. Set
|
|
`ANDROID_HOME` to the SDK. `run` always requires an explicit device and never
|
|
creates or starts one.
|
|
|
|
Debug APKs use the standard key at `~/.android/debug.keystore`, creating it
|
|
with `keytool` when absent. A release build requires the long-lived signing
|
|
identity explicitly:
|
|
|
|
```sh
|
|
IRIS_KEYSTORE_PASSWORD=... IRIS_KEY_PASSWORD=... \
|
|
cargo iris apk --release --keystore /secure/upload.jks --key-alias upload
|
|
```
|
|
|
|
APK staging and output live under
|
|
`target/iris-android/<package>[-<example>]/<debug|release>/<abi>/`; the
|
|
command's final line is the verified APK's absolute path.
|
|
|
|
Goals, in general order:
|
|
1. does what I want it to (text, images, video, animations)
|
|
2. very easy to use ignoring ergonomic ref counting
|
|
3. reasonably fast / efficient (a lot faster than electron, save battery life, try to beat iced and xilem)
|
|
|
|
## dev details
|
|
|
|
not targeting web rn cause wanna use actual nice gpu features & entire point of this is to make desktop apps / not need a web browser
|
|
|
|
general ideas trynna use rn / experiment with:
|
|
- retained mode
|
|
- specifically designed around wgpu so there's no translation
|
|
- postfix functions for most things to prevent unreadable indentation (going very well)
|
|
- events can be done directly where you draw the widgets
|
|
- almost no macros in user code & actual LSP typechecking (variadic generics if you can hear me please save us)
|
|
- relative anchor + absolute offset coord system (+ "rest" / leftover during widget layout)
|
|
- single threaded ui & pass context around to make non async usage straightforward (pretty unsure about this)
|
|
- widgets store outside of the actual rendering so they can be moved around and swapped easily (unsure about this but seems to work good for now)
|
|
|
|
under heavy initial development so not gonna try to explain status, maybe check TODO for that;
|
|
sizable chance it gets a rewrite once I know everything I need and what seems to work best
|
|
|
|
it's called iris because it's the structure around what you actually want to display and colorful
|