Skip to main content

ravenrs_widgets/
live.rs

1//! The components as the playground's interpreter sees them (feature `live`): every exposed
2//! type, how views become an [`AnyView`], adapters for the few generic functions an example
3//! uses, and a [`Runner`] for the [`Playground`](crate::Playground).
4
5use std::cell::{Cell, RefCell};
6use std::rc::Rc;
7
8use ravenrs_live::{Fallback, FromLive, Host, Record, Registry, Value, bridge};
9use ravenrs_reactive::State;
10
11use crate::context::{BuildCx, NodeId};
12use crate::{AnyView, CodeDiagnostic, Runner, Spacer, Text, View};
13
14/// How a view of type `T` goes where an [`AnyView`] goes.
15pub fn view<T: View + 'static>(registry: &mut Registry) {
16    registry.conversion::<T, AnyView>(bridge::host_name::<T>(), |view| Box::new(view));
17}
18
19impl FromLive for AnyView {
20    fn from_live(value: Value) -> Result<Self, String> {
21        match value.read() {
22            Value::Host(host) => host.take_as::<AnyView>(),
23            // A bare text where a view goes reads as the text, as `impl Into<String>` would.
24            Value::Str(text) => Ok(Box::new(Text::new(text))),
25            Value::Unit | Value::Option(None) => Ok(Box::new(Spacer::new())),
26            Value::Option(Some(inner)) => AnyView::from_live(*inner),
27            // A struct of the program with `impl View for ..`.
28            Value::Record(record) if record.has_method("build") => Ok(Box::new(LiveView { record })),
29            other => Err(format!("expected a view, found {}", other.kind())),
30        }
31    }
32}
33
34impl Fallback for AnyView {
35    fn fallback() -> Self {
36        Box::new(Spacer::new())
37    }
38}
39
40/// `Text::bound(&state, |value| ..)`: generic over the state's type, so written out here.
41fn generic_functions(registry: &mut Registry) {
42    registry.function("Text", "bound", |args: Vec<Value>| {
43        let [state, format] =
44            <[Value; 2]>::try_from(args).map_err(|_| "`bound` takes a state and a closure")?;
45        let state: State<Value> = State::<Value>::from_live(state)?;
46        let Value::Closure(format) = format.read() else { return Err("`bound` takes a closure".into()) };
47        let text = Text::bound(&state, move |value: &Value| match format.call(vec![value.clone()]) {
48            Ok(Value::Str(text)) => text,
49            Ok(other) => format!("{other:?}"),
50            Err(problem) => problem.message,
51        });
52        Ok(Value::Host(ravenrs_live::Host::new("Text", text)))
53    });
54}
55
56/// Methods every view has, from `ViewExt`, kept under `View` for any host view.
57fn view_methods(registry: &mut Registry) {
58    use crate::ViewExt;
59    use ravenrs_core::Insets;
60
61    fn this(args: &[Value]) -> Result<AnyView, String> {
62        AnyView::from_live(args.first().cloned().unwrap_or(Value::Unit))
63    }
64    fn arg<T: FromLive>(args: &[Value], i: usize) -> Result<T, String> {
65        T::from_live(args.get(i).cloned().ok_or("an argument is missing")?)
66    }
67    fn styled(view: crate::Styled<AnyView>) -> Result<Value, String> {
68        Ok(Value::Host(ravenrs_live::Host::new("Styled", view)))
69    }
70    registry.method("View", "padding", |a| styled(this(&a)?.padding(arg::<Insets>(&a, 1)?)));
71    registry.method("View", "background", |a| {
72        let fill = match a.get(1).map(Value::read) {
73            Some(Value::Host(host)) => host.take_as::<crate::Fill>()?,
74            _ => return Err("a color or a semantic color goes here".to_owned()),
75        };
76        styled(this(&a)?.background(fill))
77    });
78    registry.method("View", "frame", |a| styled(this(&a)?.frame(arg(&a, 1)?, arg(&a, 2)?)));
79    registry.method("View", "width", |a| styled(this(&a)?.width(arg(&a, 1)?)));
80    registry.method("View", "height", |a| styled(this(&a)?.height(arg(&a, 1)?)));
81    registry.method("View", "grow", |a| styled(this(&a)?.grow(arg(&a, 1)?)));
82    registry.method("View", "stretch", |a| styled(this(&a)?.stretch()));
83    registry.method("View", "centered", |a| styled(this(&a)?.centered()));
84    registry.method("View", "tooltip", |a| {
85        let view = this(&a)?.tooltip(arg::<String>(&a, 1)?);
86        Ok(Value::Host(ravenrs_live::Host::new("Tooltip", view)))
87    });
88    registry.conversion::<ravenrs_style::Semantic, crate::Fill>("Semantic", crate::Fill::from);
89    registry.conversion::<ravenrs_core::Color, crate::Fill>("Color", crate::Fill::from);
90    // `view_fn(|| ..)` builds its view each time, by calling the closure again.
91    registry.free_function("view_fn", |args: Vec<Value>| {
92        let Some(Value::Closure(make)) = args.first().map(Value::read) else {
93            return Err("`view_fn` takes a closure".into());
94        };
95        let view = crate::view_fn(move || {
96            match make.call(Vec::new()).and_then(|v| AnyView::from_live(v).map_err(|e| make.error(e))) {
97                Ok(view) => view,
98                Err(problem) => {
99                    let shown: AnyView = Box::new(Text::new(problem.message));
100                    shown
101                }
102            }
103        });
104        let view: AnyView = Box::new(view);
105        Ok(Value::Host(ravenrs_live::Host::new("AnyView", view)))
106    });
107}
108
109/// `Canvas::new(|frame, scene| ..)`: the closure draws into a scene of its own, which is then
110/// added to the real one, as a `&mut Scene` cannot leave the paint.
111fn canvas(registry: &mut Registry) {
112    use ravenrs_core::{Rect, Scene};
113    registry.function("Canvas", "new", |args: Vec<Value>| {
114        let Some(Value::Closure(draw)) = args.first().map(Value::read) else {
115            return Err("`Canvas::new` takes a closure of the frame and the scene".into());
116        };
117        let canvas = crate::Canvas::new(move |frame: Rect, scene: &mut Scene| {
118            let own = ravenrs_live::Host::new("Scene", Scene::new());
119            let frame = Value::Host(ravenrs_live::Host::cloneable("Rect", frame));
120            if let Err(problem) = draw.call(vec![frame, Value::Host(own.clone())]) {
121                ravenrs_live::report(problem);
122            }
123            if let Ok(drawn) = own.take::<Scene>() {
124                scene.extend_from_slice(drawn.primitives());
125            }
126        });
127        Ok(Value::Host(ravenrs_live::Host::new("Canvas", canvas)))
128    });
129}
130
131/// A view the program declares itself, with `impl View for Name { fn build(&self, cx) .. }`.
132/// Its `build` runs in the interpreter at every build, with a `cx` that keeps its states.
133struct LiveView {
134    record: Rc<Record>,
135}
136
137/// What the program's `build` gets as `cx`: its states, kept between builds in order, the time,
138/// and the view it hands to `cx.child`.
139#[derive(Clone)]
140struct LiveCx {
141    states: Rc<RefCell<Vec<State<Value>>>>,
142    next: Rc<Cell<usize>>,
143    now: f64,
144    wake: Rc<Cell<Option<f64>>>,
145    child: Rc<RefCell<Option<AnyView>>>,
146}
147
148impl View for LiveView {
149    fn build(&self, cx: &mut BuildCx<'_>) -> NodeId {
150        let states = cx.use_state(|| Rc::new(RefCell::new(Vec::<State<Value>>::new()))).get();
151        let live = LiveCx {
152            states,
153            next: Rc::new(Cell::new(0)),
154            now: cx.now(),
155            wake: Rc::new(Cell::new(None)),
156            child: Rc::new(RefCell::new(None)),
157        };
158        let given = Value::Host(Host::cloneable("BuildCx", live.clone()));
159        let built = Record::call_method(&self.record, "build", vec![given]);
160        if let Some(time) = live.wake.get() {
161            cx.wake_at(time);
162        }
163        let child = live.child.borrow_mut().take();
164        match (built, child) {
165            (Ok(_), Some(view)) => cx.child(&*view),
166            (Err(problem), _) => {
167                let text = Text::new(problem.to_string());
168                ravenrs_live::report(problem);
169                cx.child(&text)
170            }
171            (Ok(_), None) => cx.child(&Text::new("`build` gives its view with `cx.child(&view)`")),
172        }
173    }
174}
175
176/// What `cx` offers the program's own views.
177fn build_cx(registry: &mut Registry) {
178    fn this(args: &[Value]) -> Result<LiveCx, String> {
179        match args.first().map(Value::read) {
180            Some(Value::Host(host)) => host.take::<LiveCx>(),
181            _ => Err("this is a method of `cx`".to_owned()),
182        }
183    }
184    registry.method("BuildCx", "use_state", |args| {
185        let cx = this(&args)?;
186        let at = cx.next.get();
187        cx.next.set(at + 1);
188        if let Some(state) = cx.states.borrow().get(at) {
189            return Ok(Value::State(state.clone()));
190        }
191        let first = match args.get(1).map(Value::read) {
192            Some(Value::Closure(init)) => init.call(Vec::new()).map_err(|e| e.message)?,
193            Some(other) => other,
194            None => Value::Unit,
195        };
196        let state = State::new(first);
197        cx.states.borrow_mut().push(state.clone());
198        Ok(Value::State(state))
199    });
200    registry.method("BuildCx", "child", |args| {
201        let cx = this(&args)?;
202        let view = AnyView::from_live(args.get(1).cloned().unwrap_or(Value::Unit))?;
203        *cx.child.borrow_mut() = Some(view);
204        Ok(Value::Host(Host::cloneable("NodeId", ())))
205    });
206    registry.method("BuildCx", "now", |args| Ok(Value::Float(this(&args)?.now)));
207    registry.method("BuildCx", "wake_at", |args| {
208        let cx = this(&args)?;
209        let time = f64::from_live(args.get(1).cloned().unwrap_or(Value::Unit))?;
210        cx.wake.set(Some(cx.wake.get().map_or(time, |t| t.min(time))));
211        Ok(Value::Unit)
212    });
213}
214
215/// `Playground::new(|code: &str| ..)`: any `Runner`, written here as a closure of the program.
216fn playground(registry: &mut Registry) {
217    registry.function("Playground", "new", |args: Vec<Value>| {
218        let Some(Value::Closure(run)) = args.first().map(Value::read) else {
219            return Err("`Playground::new` takes a closure of the code".into());
220        };
221        let problem = |message: String| vec![CodeDiagnostic::error(1, 1, 1, message)];
222        let playground =
223            crate::Playground::new(move |source: &str| -> Result<AnyView, Vec<CodeDiagnostic>> {
224                let value = run.call(vec![Value::Str(source.to_owned())]).map_err(|e| problem(e.message))?;
225                match bridge::result(&value) {
226                    Ok(view) => AnyView::from_live(view).map_err(problem),
227                    Err(errors) => Err(errors
228                        .items()
229                        .map_err(problem)?
230                        .into_iter()
231                        .map(|e| match e.read() {
232                            Value::Host(host) => host.take_as::<CodeDiagnostic>().map_err(problem),
233                            other => {
234                                Err(problem(format!("expected a CodeDiagnostic, found {}", other.kind())))
235                            }
236                        })
237                        .collect::<Result<Vec<_>, _>>()?),
238                }
239            });
240        Ok(Value::Host(Host::new("Playground", playground)))
241    });
242}
243
244/// Everything the interpreter can reach in the components and the crates under them.
245pub fn live_registry() -> Registry {
246    let mut registry = Registry::new();
247    crate::live_list::expose_all(&mut registry);
248    // Made by a `macro_rules!`, so out of the generated list's sight.
249    crate::VStack::<Vec<AnyView>>::__live_expose(&mut registry);
250    crate::HStack::<Vec<AnyView>>::__live_expose(&mut registry);
251    view::<crate::VStack<Vec<AnyView>>>(&mut registry);
252    view::<crate::HStack<Vec<AnyView>>>(&mut registry);
253    generic_functions(&mut registry);
254    view_methods(&mut registry);
255    canvas(&mut registry);
256    build_cx(&mut registry);
257    playground(&mut registry);
258    registry
259}
260
261/// Runs the code of a [`Playground`](crate::Playground) with the interpreter: its first
262/// function, which returns `impl View`, is called and shown. The program's own `use` lines
263/// are read and left alone; what it does not read comes back as diagnostics with their place.
264///
265/// ```
266/// use ravenrs_widgets::{LiveRunner, Runner};
267///
268/// let view = LiveRunner::new().run(r#"fn hello() -> impl View { Text::new("hello") }"#);
269/// assert!(view.is_ok());
270/// ```
271#[derive(Clone)]
272pub struct LiveRunner {
273    registry: Rc<Registry>,
274}
275
276impl std::fmt::Debug for LiveRunner {
277    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
278        f.debug_struct("LiveRunner").finish_non_exhaustive()
279    }
280}
281
282impl Default for LiveRunner {
283    fn default() -> Self {
284        Self::new()
285    }
286}
287
288impl LiveRunner {
289    /// A runner over [`live_registry`].
290    pub fn new() -> Self {
291        Self::with_registry(live_registry())
292    }
293
294    /// A runner over a registry of the app's own, such as one with more types added.
295    pub fn with_registry(registry: Registry) -> Self {
296        Self { registry: Rc::new(registry) }
297    }
298}
299
300fn diagnostic(error: ravenrs_live::LiveError) -> CodeDiagnostic {
301    CodeDiagnostic::error(error.line, error.column, error.length, error.message)
302}
303
304impl Runner for LiveRunner {
305    fn run(&self, source: &str) -> Result<AnyView, Vec<CodeDiagnostic>> {
306        let value = ravenrs_live::run(source, None, self.registry.clone())
307            .map_err(|errors| errors.into_iter().map(diagnostic).collect::<Vec<_>>())?;
308        AnyView::from_live(value).map_err(|message| vec![CodeDiagnostic::error(1, 1, 1, message)])
309    }
310
311    fn take_output(&self) -> crate::RunOutput {
312        let problems = ravenrs_live::late_problems().into_iter().map(diagnostic).collect();
313        crate::RunOutput::new(ravenrs_live::printed(), problems)
314    }
315
316    fn watch(&self, wake: Rc<dyn Fn()>) {
317        ravenrs_live::on_output(move || wake());
318    }
319}