use super::Composition;
use super::composer::{Composer, EventCallback, ROOT};
use super::seam::{Batch, Runtime};
use super::{drain_graveyard, effects, state, with_composer};
use crate::boundary::{Host, request_frame_from_worker};
use crate::protocol::{HostEvent, ProtocolError};
use crate::schema::{EventPayload, Theme};
use crate::{KeyEvent, RangeRequest};
use std::any::Any;
use std::rc::Rc;
use std::sync::Arc;
pub struct Recomposer {
content: Rc<dyn Fn()>,
composition: Rc<Composition>,
_subscriptions: Vec<Box<dyn Any>>,
}
impl Recomposer {
pub fn new(content: impl Fn() + 'static) -> Self {
let composition = Composition::new();
let (window, design, permission) = {
let composer = composition.composer.borrow();
(
composer.ambient.window.clone(),
composer.ambient.design.clone(),
composer.ambient.permission.clone(),
)
};
let subscriptions: Vec<Box<dyn Any>> = vec![
Box::new(crate::window::subscribe(Arc::new(move || {
window.set(crate::window::window_size())
}))),
Box::new(crate::design::subscribe(Arc::new(move || {
design.set(crate::design::design_system())
}))),
Box::new(crate::notification::subscribe_permission(Arc::new(
move || permission.set(crate::notification::notification_permission()),
))),
];
Self {
content: Rc::new(content),
composition,
_subscriptions: subscriptions,
}
}
pub fn arena(&self) -> (*const u8, usize) {
self.composition.composer.borrow().writer.arena()
}
pub fn size_token(&self, node_id: u32) -> Option<u32> {
self.composition
.composer
.borrow()
.writer
.size_token(node_id)
}
pub fn rebuild(&mut self) {
let _composition = self.composition.enter();
with_composer(|composer| {
composer.abandon_composition();
composer.begin_root();
});
(self.content)();
with_composer(Composer::end_root);
self.after_composition();
}
pub fn render(&mut self, frame_time_nanos: Option<u64>) {
let _composition = self.composition.enter();
if let Some(now) = frame_time_nanos {
with_composer(|composer| composer.executor.deliver_frame(now));
}
effects::poll_woken();
self.recompose();
}
pub fn handle_event(&mut self, event: &HostEvent<'_>) -> Result<i64, ProtocolError> {
let _composition = self.composition.enter();
let Some((node, callback)) = with_composer(|composer| {
composer
.handlers
.get(&event.handler_id)
.map(|entry| (entry.node, entry.callback.clone()))
})
.flatten() else {
return Err(ProtocolError::InvalidValueKind(0));
};
if node != event.node_id {
return Err(ProtocolError::InvalidValueKind(0));
}
deliver(&callback, &event.payload)
}
pub fn run_in_context(&mut self, action: &mut dyn FnMut()) {
let _composition = self.composition.enter();
action();
}
fn recompose(&mut self) {
with_composer(Composer::abandon_composition);
state::apply_state_changes();
let root = with_composer(|composer| composer.root_invalid()).unwrap_or(false);
if root {
with_composer(Composer::begin_root);
(self.content)();
with_composer(Composer::end_root);
}
let invalid = with_composer(Composer::take_invalid).unwrap_or_default();
for group in invalid {
if group == ROOT {
continue;
}
let restart = with_composer(|composer| composer.prepare_restart(group)).flatten();
if let Some(restart) = restart {
restart();
with_composer(Composer::finish_restart);
}
}
self.after_composition();
}
fn after_composition(&mut self) {
drain_graveyard();
effects::apply_effects();
drain_graveyard();
effects::poll_woken();
let pending = with_composer(|composer| {
!composer
.signal
.dirty
.lock()
.unwrap_or_else(|poison| poison.into_inner())
.is_empty()
|| !composer.invalid.is_empty()
})
.unwrap_or(false);
if pending {
request_frame_from_worker();
}
}
}
pub struct ComposeHost(Host);
impl ComposeHost {
pub fn new(content: fn()) -> Self {
Self(Host::new(recomposer_for(content)))
}
pub fn with_theme(content: fn(), theme: Theme) -> Self {
Self(Host::with_theme(recomposer_for(content), theme))
}
pub fn with_content(content: impl Fn() + Send + Sync + 'static) -> Self {
let content = Arc::new(content);
Self(Host::new(move || {
let content = Arc::clone(&content);
Box::new(Recomposer::new(move || content())) as Box<dyn Runtime>
}))
}
}
impl std::ops::Deref for ComposeHost {
type Target = Host;
fn deref(&self) -> &Host {
&self.0
}
}
impl std::ops::DerefMut for ComposeHost {
fn deref_mut(&mut self) -> &mut Host {
&mut self.0
}
}
pub fn recomposer_for(content: fn()) -> impl Fn() -> Box<dyn Runtime> + Send + Sync + 'static {
move || Box::new(Recomposer::new(content)) as Box<dyn Runtime>
}
impl crate::LaunchBuilder {
pub fn launch(self, content: fn()) {
self.launch_runtime(recomposer_for(content));
}
pub fn try_launch(self, content: fn()) -> i32 {
self.try_launch_runtime(recomposer_for(content))
}
pub fn application(self, content: fn()) {
self.launch(content);
}
}
pub fn launch(content: fn()) {
crate::LaunchBuilder::new().launch(content);
}
pub fn application(content: fn()) {
launch(content);
}
fn deliver(callback: &EventCallback, payload: &EventPayload<'_>) -> Result<i64, ProtocolError> {
match (callback, payload) {
(
EventCallback::Unit(handler),
EventPayload::Clicked | EventPayload::FocusLost | EventPayload::FilesEntered,
) => (*handler.borrow_mut())(),
(EventCallback::Text(handler), EventPayload::TextChanged(text))
| (EventCallback::Text(handler), EventPayload::TextSubmitted(text)) => {
(*handler.borrow_mut())((*text).to_owned())
}
(
EventCallback::Key(handler),
EventPayload::KeyDown {
key,
shift_key,
ctrl_key,
alt_key,
meta_key,
},
) => {
let event = KeyEvent::new(*key, *shift_key, *ctrl_key, *alt_key, *meta_key);
(*handler.borrow_mut())(event.clone());
return Ok(i64::from(event.consumed()));
}
(
EventCallback::Pointer(handler),
EventPayload::Pointer {
kind,
pointer_id,
pointer_type,
x,
y,
buttons,
button,
modifiers,
click_count,
delta_x,
delta_y,
delta_mode,
timestamp_nanos,
},
) => {
let event = crate::PointerEvent::new(
*kind,
*pointer_id,
*pointer_type,
*x,
*y,
*buttons,
*button,
*modifiers,
*click_count,
*delta_x,
*delta_y,
*delta_mode,
*timestamp_nanos,
);
(*handler.borrow_mut())(event.clone());
return Ok(i64::from(event.consumed()));
}
(EventCallback::Value(handler), EventPayload::ValueChanged(value)) => {
(*handler.borrow_mut())(*value)
}
(EventCallback::Range(handler), EventPayload::RangeRequested { start, count }) => {
(*handler.borrow_mut())(RangeRequest::new(*start, *count))
}
(EventCallback::Files(handler), EventPayload::FilesDropped(paths)) => {
(*handler.borrow_mut())(crate::FileDrop::new(paths))
}
_ => return Err(ProtocolError::InvalidValueKind(0)),
}
Ok(0)
}
impl Composer {
pub(crate) fn abandon_composition(&mut self) {
self.clear_walk();
}
}
impl Runtime for Recomposer {
fn batch(&self) -> &Batch {
let composer = self.composition.composer.as_ptr();
unsafe { (*composer).writer.batch() }
}
fn batch_mut(&mut self) -> &mut Batch {
let composer = self.composition.composer.as_ptr();
unsafe { (*composer).writer.batch_mut() }
}
fn rebuild(&mut self) {
Recomposer::rebuild(self);
}
fn render(&mut self, frame_time_nanos: Option<u64>) {
Recomposer::render(self, frame_time_nanos);
}
fn handle_event(&mut self, event: &HostEvent<'_>) -> Result<i64, ProtocolError> {
Recomposer::handle_event(self, event)
}
fn size_token(&self, node_id: u32) -> Option<u32> {
Recomposer::size_token(self, node_id)
}
fn run_in_context(&mut self, action: &mut dyn FnMut()) {
Recomposer::run_in_context(self, action);
}
}