pub mod callback;
pub mod clipboard;
pub mod context;
pub mod cursor;
pub mod diagnostics;
pub mod effect;
pub mod focus;
pub(crate) mod global;
pub(crate) mod guard;
pub mod into_signal;
pub mod invalidation;
pub mod memo;
pub mod owner;
mod password;
pub mod prop;
pub mod runtime;
pub mod service;
pub mod signal;
pub(crate) mod state;
pub mod storage;
pub mod timer;
mod trigger;
pub(crate) use clipboard::paste_into;
pub use clipboard::{
PastedText, SelectionKind, clipboard_copy, clipboard_has_content, clipboard_paste,
primary_copy, primary_paste, take_clipboard_change, take_primary_change,
};
pub use context::{
expect_context, has_context, provide_context, provide_signal_context, use_context, with_context,
};
pub use cursor::CursorIcon;
pub(crate) use cursor::take_cursor_change;
pub use effect::create_effect;
pub(crate) use focus::{has_focus, release_focus_if_within, request_focus};
pub(crate) use into_signal::converts;
#[doc(hidden)]
pub use into_signal::{
ClosureMarker, ConvertedSignalMarker, LossyMarker, MemoMarker, RwSignalMarker, SignalMarker,
ValueMarker,
};
pub use into_signal::{IntoSignal, IntoVal};
pub use prop::Prop;
pub use crate::jobs::JobType;
pub use invalidation::with_signal_tracking;
pub use memo::{Memo, create_memo};
pub use owner::{OwnerId, current_owner, dispose_owner, on_cleanup};
pub(crate) use owner::{create_root_owner, dispose_owner_now, under_owner, with_owner};
pub use password::{Password, create_password};
pub use trigger::{Trigger, create_trigger};
#[doc(hidden)]
pub mod __internal {
pub use super::owner::{OwnerId, dispose_owner_now as dispose_owner, with_owner};
pub use super::runtime::batch;
}
pub use callback::Callback;
pub(crate) use runtime::{flush_bg_writes, reset_bg_writes};
pub use service::{Service, ServiceContext, create_service, create_task};
pub use signal::{RwSignal, Signal, WriteSignal, create_derived, create_signal, create_stored};
pub use timer::{TimerHandle, set_interval, set_timeout};
pub(crate) fn reset_reactive() {
state::reset();
diagnostics::reset();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn resetting_leaves_nothing_for_the_next_app_to_inherit() {
owner::create_root_owner();
let _signal = create_signal(1u32);
provide_context(7u32);
assert!(
storage::live_signal_count() > 0,
"the App under test has state to wipe"
);
reset_reactive();
assert_eq!(
storage::live_signal_count(),
0,
"a signal from the last App is still in the arena the next one allocates from"
);
assert!(
owner::current_owner().is_none(),
"the next App would file its setup under a scope that no longer exists"
);
owner::create_root_owner();
assert_eq!(
use_context::<u32>(),
None,
"the next App would read the last one's declarations"
);
}
}
#[cfg(test)]
mod bench {
use std::time::{Duration, Instant};
use super::*;
fn best_of_five(mut round: impl FnMut() -> Duration) -> Duration {
(0..5).map(|_| round()).min().expect("five rounds")
}
#[test]
#[ignore]
fn an_effect_rerun() {
let rounds = 1_000_000u32;
owner::create_root_owner();
let source = create_signal(0u32);
let seen = std::rc::Rc::new(std::cell::Cell::new(0u32));
let recorder = std::rc::Rc::clone(&seen);
create_effect(move || recorder.set(source.get()));
let best = best_of_five(|| {
let started = Instant::now();
for round in 0..rounds {
source.set(round);
}
started.elapsed()
});
assert_eq!(seen.get(), rounds - 1, "the effect ran");
println!("{rounds} effect re-runs: {best:?}");
}
#[test]
#[ignore]
fn a_frame_of_three_hundred_rows() {
use crate::layout::Constraints;
use crate::renderer::RenderNode;
use crate::tree::Tree;
use crate::widgets::widget::Color;
use crate::widgets::{Widget, container};
owner::create_root_owner();
let width = create_signal(40.0f32);
let rows: Vec<_> = (0..300)
.map(|row| {
container()
.width(move || width.get() + row as f32 % 3.0)
.height(move || 12.0 + row as f32 % 2.0)
.background(move || Color::rgb(0.1 + width.get() / 400.0, 0.2, 0.3))
.padding(move || width.get() / 20.0)
})
.collect();
let mut tree = Tree::new();
let root = tree.register(Box::new(container().children(rows)) as Box<dyn Widget>);
tree.with_widget_mut(root, |w, id, t| w.register_children(t, id));
let frames = 200u32;
let best = best_of_five(|| {
let started = Instant::now();
for frame in 0..frames {
width.set(40.0 + (frame % 7) as f32);
tree.layout_widget(root, Constraints::new(0.0, 0.0, 400.0, 4000.0));
let mut node = RenderNode::new(root.as_u64());
tree.paint_widget(root, &mut node, 1.0);
std::hint::black_box(&node);
}
started.elapsed() / frames
});
println!("a frame of 300 rows: {best:?}");
}
#[test]
#[ignore]
fn a_derived_read() {
owner::create_root_owner();
let source = create_signal(1u32);
let derived = create_derived(move || source.get() + 1);
let rounds = 1_000_000u32;
let best = |derived: Signal<u32>| -> Duration {
best_of_five(|| {
let started = Instant::now();
for _ in 0..rounds {
std::hint::black_box(derived.get());
}
started.elapsed()
})
};
for _ in 0..100_000 {
std::hint::black_box(derived.get());
}
let from_its_own_scope = best(derived);
let (from_elsewhere, elsewhere) = owner::with_owner(|| best(derived));
owner::dispose_owner_now(elsewhere);
println!(
"{rounds} derived reads: {from_its_own_scope:?} from their own \
scope, {from_elsewhere:?} from another"
);
}
}