use crate::view::element::IntoElement;
use crate::view::render::{Component, ViewContext};
use crate::view::state::StateStore;
use crate::view::ui::Ui;
use crate::view::widget_store::WidgetStore;
use std::marker::PhantomData;
pub(crate) struct FnApp<F, R> {
build: F,
state: StateStore,
widgets: WidgetStore,
marker: PhantomData<R>,
}
impl<F, R> FnApp<F, R> {
pub(crate) fn new(build: F) -> Self {
Self { build, state: StateStore::default(), widgets: WidgetStore::default(), marker: PhantomData }
}
}
impl<F, R> Component for FnApp<F, R>
where
F: FnMut(&mut Ui) -> R + 'static,
R: IntoElement + 'static,
{
fn view(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let theme = cx.theme;
let viewport = cx.viewport;
let entities = cx.entities_mut();
let mut ui = Ui::new(theme, viewport, entities, &mut self.state, &mut self.widgets);
(self.build)(&mut ui).into_element()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::view::prelude::*;
use crate::view::test_context::TestWindow;
use std::cell::Cell;
use std::rc::Rc;
#[test]
fn una_closure_lee_el_estado_y_se_redibuja_al_pulsar() {
let seen = Rc::new(Cell::new(0u32));
let capture = seen.clone();
let app = FnApp::new(move |ui: &mut Ui| {
let count = ui.state("count", 0u32);
capture.set(count.get());
ui.page().center().child(ui.button("Sumar").on_click(count.update(|n| *n += 1)))
});
let (mut test, _) = TestWindow::new(|_| app);
assert_eq!(seen.get(), 0);
let [x, y, width, height] = test.window.scene().quads.last().expect("el botón pinta").bounds;
test.simulate_click(x + width / 2.0, y + height / 2.0);
assert_eq!(seen.get(), 1, "el handler mutó el estado y pidió otro frame");
}
#[test]
fn un_componente_conserva_su_estado_entre_frames() {
let seen = Rc::new(Cell::new(0u32));
let capture = seen.clone();
let app = FnApp::new(move |ui: &mut Ui| {
let capture = capture.clone();
ui.component(
"panel",
|| 0u32,
move |ui, value, handle| {
capture.set(*value);
ui.page().center().child(ui.button("Sumar").on_click(handle.update(|n| *n += 1)))
},
)
});
let (mut test, _) = TestWindow::new(|_| app);
assert_eq!(seen.get(), 0);
let [x, y, width, height] = test.window.scene().quads.last().expect("el botón pinta").bounds;
test.simulate_click(x + width / 2.0, y + height / 2.0);
assert_eq!(seen.get(), 1, "el componente conserva su valor y se redibuja");
}
}