126 lines
4.3 KiB
Rust
126 lines
4.3 KiB
Rust
use ruin_app::prelude::*;
|
|
|
|
// This is the aspirational author-facing source we eventually want to support:
|
|
//
|
|
// ```ignore
|
|
// #[ruin_runtime::async_main]
|
|
// async fn main() -> ruin::Result<()> {
|
|
// App::new()
|
|
// .window(Window::new().title("RUIN Counter").size(960.0, 640.0))
|
|
// .app_id("dev.ruin.counter")
|
|
// .mount(view! { CounterApp {} })
|
|
// #[component]
|
|
// fn CounterApp(title: &'static str) -> impl View {
|
|
// let count = use_signal(|| 0);
|
|
// let doubled = use_memo(move || count.get() * 2);
|
|
//
|
|
// use_window_title(move || format!("{title} ({})", count.get()));
|
|
//
|
|
// view! {
|
|
// column(gap = 16, padding = 24) {
|
|
// text(role = TextRole::Heading(1), size = 32, weight = FontWeight::Semibold) {
|
|
// title
|
|
// }
|
|
//
|
|
// row(gap = 8) {
|
|
// button(on_press = move |_| count.update(|value| *value -= 1)) { "-1" }
|
|
// button(on_press = move |_| count.set(0)) { "Reset" }
|
|
// button(on_press = move |_| count.update(|value| *value += 1)) { "+1" }
|
|
// }
|
|
//
|
|
// block(padding = 16, gap = 8, background = surfaces::raised()) {
|
|
// text(size = 18) { "count = "; count }
|
|
// text(color = colors::muted()) { "double = "; doubled }
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// ```
|
|
//
|
|
// The hand-written code below is the kind of lower-level shape the proc macro should expand to.
|
|
//
|
|
// Dev notes:
|
|
// - `mount` needs to take _any_ component. It should work with an arbitrary expansion of `view!`, not just a
|
|
// specially-designated root component.
|
|
// - The `RootComponent` trait is awkward and unnecessary.
|
|
// - The component render function needs to run as-defined. Hook lifecycles will be managed by runtime context. We don't
|
|
// want to get into the business of significantly transforming the execution semantics of the component function.
|
|
// - The logic is that the syntax inside of `view!` expands to an instantiation of a struct that implements `Component`,
|
|
// by calling the builder associated functions. Each parameter to the render function becomes a method of the builder,
|
|
// with `children` as a special finalizing method and reserved property name. This is the only way to stay sane.
|
|
|
|
#[ruin_runtime::async_main]
|
|
async fn main() -> ruin_app::Result<()> {
|
|
let title = "RUIN Counter";
|
|
App::new()
|
|
.window(
|
|
Window::new()
|
|
.title(title)
|
|
.app_id("dev.ruin.counter")
|
|
.size(960.0, 640.0),
|
|
)
|
|
.mount(CounterApp::builder().title(title).children(()))
|
|
.run()
|
|
.await
|
|
}
|
|
|
|
struct CounterApp {
|
|
title: &'static str,
|
|
}
|
|
|
|
const DECREMENT_BUTTON_ID: ElementId = ElementId::new(1);
|
|
const RESET_BUTTON_ID: ElementId = ElementId::new(2);
|
|
const INCREMENT_BUTTON_ID: ElementId = ElementId::new(3);
|
|
|
|
#[derive(Default)]
|
|
struct __CounterAppBuilder {
|
|
title: Option<&'static str>,
|
|
}
|
|
|
|
impl __CounterAppBuilder {
|
|
fn title(&mut self, title: &'static str) -> &self {
|
|
self.title = Some(title);
|
|
self
|
|
}
|
|
|
|
fn children(self, _children: ()) -> Result<CounterApp, anyhow::Error> {
|
|
CounterApp::from_builder(self)
|
|
}
|
|
}
|
|
|
|
impl Component for CounterApp {
|
|
type Builder = __CounterAppBuilder;
|
|
|
|
fn builder() -> Self::Builder {
|
|
__CounterAppBuilder::default()
|
|
}
|
|
|
|
fn from_builder(builder: Self::Builder) -> Result<Self, anyhow::Error> {
|
|
Ok(Self {
|
|
title: builder
|
|
.title
|
|
.ok_or_else(|| anyhow::anyhow!("Missing required property `title`"))?,
|
|
})
|
|
}
|
|
|
|
fn render(&self) -> View {
|
|
__render_CounterApp(self.title)
|
|
}
|
|
}
|
|
|
|
#[allow(non_snake_case)]
|
|
fn __render_CounterApp(title: &'static str) -> View {
|
|
// The state will be managed by the runtime, like how it's done in react. When rerunning the render function,
|
|
// use_signal and use_memo will return the same state/memo instances as the previous run, so the state is preserved
|
|
// across renders as appropriate. This will require correlating the state/memo instances to call traces using
|
|
// thread-local state on the UI thread.
|
|
let count = use_signal(|| 0);
|
|
let doubled = use_memo(move || count.get() * 2);
|
|
|
|
use_window_title(move || format!("{title} ({})", count.get()));
|
|
|
|
// Expansion of view macro to return a concrete view goes here.
|
|
// ...
|
|
// ...
|
|
}
|