wip: better desktop support
This commit is contained in:
1 parent
19df1bda10
commit
25a8411485
11 files changed
+519
-586
No files matched your search
+22
-21
@@ -643,9 +643,9 @@ impl<'a> NodeFactory<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fragment_from_iter(
|
||||
pub fn fragment_from_iter<'b, 'c>(
|
||||
self,
|
||||
node_iter: impl IntoIterator<Item = impl IntoVNode<'a>>,
|
||||
node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,
|
||||
) -> VNode<'a> {
|
||||
let bump = self.bump;
|
||||
let mut nodes = bumpalo::collections::Vec::new_in(bump);
|
||||
@@ -662,24 +662,25 @@ impl<'a> NodeFactory<'a> {
|
||||
|
||||
let children = nodes.into_bump_slice();
|
||||
|
||||
// TODO
|
||||
// We need a dedicated path in the rsx! macro that will trigger the "you need keys" warning
|
||||
//
|
||||
// if cfg!(debug_assertions) {
|
||||
// if children.len() > 1 {
|
||||
// if children.last().unwrap().key().is_none() {
|
||||
// log::error!(
|
||||
// r#"
|
||||
// Warning: Each child in an array or iterator should have a unique "key" prop.
|
||||
// Not providing a key will lead to poor performance with lists.
|
||||
// See docs.rs/dioxus for more information.
|
||||
// ---
|
||||
// To help you identify where this error is coming from, we've generated a backtrace.
|
||||
// "#,
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// todo: add a backtrace
|
||||
if cfg!(debug_assertions) && children.len() > 1 && children.last().unwrap().key().is_none()
|
||||
{
|
||||
// todo: rsx! calls get turned into fragments which means they always trip this error
|
||||
//
|
||||
//
|
||||
// use backtrace::Backtrace;
|
||||
// let bt = Backtrace::new();
|
||||
|
||||
log::error!(
|
||||
r#"
|
||||
Warning: Each child in an array or iterator should have a unique "key" prop.
|
||||
Not providing a key will lead to poor performance with lists.
|
||||
See docs.rs/dioxus for more information.
|
||||
-------------
|
||||
"#,
|
||||
// bt
|
||||
);
|
||||
}
|
||||
|
||||
VNode::Fragment(VFragment {
|
||||
children,
|
||||
@@ -820,7 +821,7 @@ impl<'a> IntoVNode<'a> for Option<LazyNodes<'a, '_>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoVNode<'a> for LazyNodes<'a, '_> {
|
||||
impl<'a, 'b> IntoVNode<'a> for LazyNodes<'a, 'b> {
|
||||
fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
|
||||
self.call(cx)
|
||||
}
|
||||
|
||||
@@ -196,6 +196,7 @@ impl Scope {
|
||||
let chan = self.sender.clone();
|
||||
let id = self.scope_id();
|
||||
Rc::new(move || {
|
||||
log::debug!("set on channel an update for scope {:?}", id);
|
||||
let _ = chan.unbounded_send(SchedulerMsg::Immediate(id));
|
||||
})
|
||||
}
|
||||
@@ -303,10 +304,18 @@ impl Scope {
|
||||
|
||||
/// Pushes the future onto the poll queue to be polled
|
||||
/// The future is forcibly dropped if the component is not ready by the next render
|
||||
pub fn push_task<'src, F: Future<Output = ()> + 'src>(
|
||||
pub fn push_task<'src, F: Future<Output = ()>>(
|
||||
&'src self,
|
||||
mut fut: impl FnOnce() -> F + 'src,
|
||||
) -> usize {
|
||||
fut: impl FnOnce() -> F + 'src,
|
||||
) -> usize
|
||||
where
|
||||
F::Output: 'src,
|
||||
F: 'src,
|
||||
{
|
||||
self.sender
|
||||
.unbounded_send(SchedulerMsg::TaskPushed(self.our_arena_idx))
|
||||
.unwrap();
|
||||
|
||||
// allocate the future
|
||||
let fut = fut();
|
||||
let fut: &mut dyn Future<Output = ()> = self.bump().alloc(fut);
|
||||
@@ -318,6 +327,7 @@ impl Scope {
|
||||
let self_ref_fut = unsafe { std::mem::transmute(boxed_fut) };
|
||||
|
||||
let mut items = self.items.borrow_mut();
|
||||
|
||||
items.tasks.push(self_ref_fut);
|
||||
items.tasks.len() - 1
|
||||
}
|
||||
|
||||
@@ -133,8 +133,6 @@ impl ScopeArena {
|
||||
|
||||
let any_item = self.scopes.borrow_mut().insert(new_scope_id, scope);
|
||||
debug_assert!(any_item.is_none());
|
||||
|
||||
new_scope_id
|
||||
} else {
|
||||
let (node_capacity, hook_capacity) = {
|
||||
let heuristics = self.heuristics.borrow();
|
||||
@@ -196,9 +194,9 @@ impl ScopeArena {
|
||||
|
||||
let any_item = self.scopes.borrow_mut().insert(new_scope_id, scope);
|
||||
debug_assert!(any_item.is_none());
|
||||
|
||||
new_scope_id
|
||||
}
|
||||
|
||||
new_scope_id
|
||||
}
|
||||
|
||||
pub fn try_remove(&self, id: &ScopeId) -> Option<()> {
|
||||
|
||||
@@ -260,87 +260,45 @@ impl VirtualDom {
|
||||
/// Waits for the scheduler to have work
|
||||
/// This lets us poll async tasks during idle periods without blocking the main thread.
|
||||
pub async fn wait_for_work(&mut self) {
|
||||
// todo: poll the events once even if there is work to do to prevent starvation
|
||||
|
||||
// if there's no futures in the virtualdom, just wait for a scheduler message and put it into the queue to be processed
|
||||
if self.scopes.pending_futures.borrow().is_empty() {
|
||||
self.pending_messages
|
||||
.push_front(self.receiver.next().await.unwrap());
|
||||
} else {
|
||||
struct PollTasks<'a> {
|
||||
scopes: &'a mut ScopeArena,
|
||||
loop {
|
||||
if !self.dirty_scopes.is_empty() && self.pending_messages.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
impl<'a> Future for PollTasks<'a> {
|
||||
type Output = ();
|
||||
if self.pending_messages.is_empty() {
|
||||
if self.scopes.pending_futures.borrow().is_empty() {
|
||||
self.pending_messages
|
||||
.push_front(self.receiver.next().await.unwrap());
|
||||
} else {
|
||||
use futures_util::future::{select, Either};
|
||||
|
||||
fn poll(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> Poll<Self::Output> {
|
||||
let mut all_pending = true;
|
||||
|
||||
let mut unfinished_tasks: SmallVec<[_; 10]> = smallvec::smallvec![];
|
||||
let mut scopes_to_clear: SmallVec<[_; 10]> = smallvec::smallvec![];
|
||||
|
||||
// Poll every scope manually
|
||||
for fut in self.scopes.pending_futures.borrow().iter() {
|
||||
let scope = self
|
||||
.scopes
|
||||
.get_scope(fut)
|
||||
.expect("Scope should never be moved");
|
||||
|
||||
let mut items = scope.items.borrow_mut();
|
||||
|
||||
// really this should just be retain_mut but that doesn't exist yet
|
||||
while let Some(mut task) = items.tasks.pop() {
|
||||
// todo: does this make sense?
|
||||
// I don't usually write futures by hand
|
||||
// I think the futures neeed to be pinned using bumpbox or something
|
||||
// right now, they're bump allocated so this shouldn't matter anyway - they're not going to move
|
||||
let task_mut = task.as_mut();
|
||||
let unpinned = unsafe { Pin::new_unchecked(task_mut) };
|
||||
|
||||
if unpinned.poll(cx).is_ready() {
|
||||
all_pending = false
|
||||
} else {
|
||||
unfinished_tasks.push(task);
|
||||
}
|
||||
}
|
||||
|
||||
if unfinished_tasks.is_empty() {
|
||||
scopes_to_clear.push(*fut);
|
||||
}
|
||||
|
||||
items.tasks.extend(unfinished_tasks.drain(..));
|
||||
}
|
||||
|
||||
for scope in scopes_to_clear {
|
||||
self.scopes.pending_futures.borrow_mut().remove(&scope);
|
||||
}
|
||||
|
||||
// Resolve the future if any singular task is ready
|
||||
match all_pending {
|
||||
true => Poll::Pending,
|
||||
false => Poll::Ready(()),
|
||||
match select(PollTasks(&mut self.scopes), self.receiver.next()).await {
|
||||
Either::Left((_, _)) => {}
|
||||
Either::Right((msg, _)) => self.pending_messages.push_front(msg.unwrap()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Poll both the futures and the scheduler message queue simulataneously
|
||||
use futures_util::future::{select, Either};
|
||||
while let Ok(Some(msg)) = self.receiver.try_next() {
|
||||
self.pending_messages.push_front(msg);
|
||||
}
|
||||
|
||||
let scheduler_fut = self.receiver.next();
|
||||
let tasks_fut = PollTasks {
|
||||
scopes: &mut self.scopes,
|
||||
};
|
||||
|
||||
match select(tasks_fut, scheduler_fut).await {
|
||||
// Futures don't generate work
|
||||
Either::Left((_, _)) => {}
|
||||
|
||||
// Save these messages in FIFO to be processed later
|
||||
Either::Right((msg, _)) => self.pending_messages.push_front(msg.unwrap()),
|
||||
if let Some(msg) = self.pending_messages.pop_back() {
|
||||
match msg {
|
||||
// just keep looping, the task is now saved but we should actually poll it
|
||||
SchedulerMsg::TaskPushed(id) => {
|
||||
self.scopes.pending_futures.borrow_mut().insert(id);
|
||||
}
|
||||
SchedulerMsg::UiEvent(event) => {
|
||||
if let Some(element) = event.element {
|
||||
self.scopes.call_listener_with_bubbling(event, element);
|
||||
}
|
||||
}
|
||||
SchedulerMsg::Immediate(s) => {
|
||||
self.dirty_scopes.insert(s);
|
||||
}
|
||||
SchedulerMsg::Suspended { scope } => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -393,36 +351,8 @@ impl VirtualDom {
|
||||
pub fn work_with_deadline(&mut self, mut deadline: impl FnMut() -> bool) -> Vec<Mutations> {
|
||||
let mut committed_mutations = vec![];
|
||||
|
||||
while self.has_any_work() {
|
||||
while let Ok(Some(msg)) = self.receiver.try_next() {
|
||||
self.pending_messages.push_front(msg);
|
||||
}
|
||||
|
||||
while let Some(msg) = self.pending_messages.pop_back() {
|
||||
match msg {
|
||||
// TODO: Suspsense
|
||||
SchedulerMsg::Immediate(id) => {
|
||||
self.dirty_scopes.insert(id);
|
||||
}
|
||||
SchedulerMsg::Suspended { node, scope } => {
|
||||
todo!("should wire up suspense")
|
||||
// self.suspense_scopes.insert(id);
|
||||
}
|
||||
SchedulerMsg::UiEvent(event) => {
|
||||
if let Some(element) = event.element {
|
||||
log::info!("Calling listener {:?}, {:?}", event.scope_id, element);
|
||||
|
||||
self.scopes.call_listener_with_bubbling(event, element);
|
||||
while let Ok(Some(dirty_scope)) = self.receiver.try_next() {
|
||||
self.pending_messages.push_front(dirty_scope);
|
||||
}
|
||||
} else {
|
||||
log::debug!("User event without a targetted ElementId. Not currently supported.\nUnsure how to proceed. {:?}", event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while !self.dirty_scopes.is_empty() {
|
||||
log::debug!("working with deadline");
|
||||
let scopes = &self.scopes;
|
||||
let mut diff_state = DiffState::new(scopes);
|
||||
|
||||
@@ -616,7 +546,10 @@ pub enum SchedulerMsg {
|
||||
// setstate
|
||||
Immediate(ScopeId),
|
||||
|
||||
Suspended { scope: ScopeId, node: NodeLink },
|
||||
// an async task pushed from an event handler (or just spawned)
|
||||
TaskPushed(ScopeId),
|
||||
|
||||
Suspended { scope: ScopeId },
|
||||
}
|
||||
|
||||
/// User Events are events that are shuttled from the renderer into the VirtualDom trhough the scheduler channel.
|
||||
@@ -720,3 +653,55 @@ pub enum EventPriority {
|
||||
/// This is considered "idle" work or "background" work.
|
||||
Low = 0,
|
||||
}
|
||||
|
||||
struct PollTasks<'a>(&'a mut ScopeArena);
|
||||
|
||||
impl<'a> Future for PollTasks<'a> {
|
||||
type Output = ();
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
|
||||
let mut all_pending = true;
|
||||
|
||||
let mut unfinished_tasks: SmallVec<[_; 10]> = smallvec::smallvec![];
|
||||
let mut scopes_to_clear: SmallVec<[_; 10]> = smallvec::smallvec![];
|
||||
|
||||
// Poll every scope manually
|
||||
for fut in self.0.pending_futures.borrow().iter() {
|
||||
let scope = self.0.get_scope(fut).expect("Scope should never be moved");
|
||||
|
||||
let mut items = scope.items.borrow_mut();
|
||||
|
||||
// really this should just be retain_mut but that doesn't exist yet
|
||||
while let Some(mut task) = items.tasks.pop() {
|
||||
// todo: does this make sense?
|
||||
// I don't usually write futures by hand
|
||||
// I think the futures neeed to be pinned using bumpbox or something
|
||||
// right now, they're bump allocated so this shouldn't matter anyway - they're not going to move
|
||||
let task_mut = task.as_mut();
|
||||
let unpinned = unsafe { Pin::new_unchecked(task_mut) };
|
||||
|
||||
if unpinned.poll(cx).is_ready() {
|
||||
all_pending = false
|
||||
} else {
|
||||
unfinished_tasks.push(task);
|
||||
}
|
||||
}
|
||||
|
||||
if unfinished_tasks.is_empty() {
|
||||
scopes_to_clear.push(*fut);
|
||||
}
|
||||
|
||||
items.tasks.extend(unfinished_tasks.drain(..));
|
||||
}
|
||||
|
||||
for scope in scopes_to_clear {
|
||||
self.0.pending_futures.borrow_mut().remove(&scope);
|
||||
}
|
||||
|
||||
// Resolve the future if any singular task is ready
|
||||
match all_pending {
|
||||
true => Poll::Pending,
|
||||
false => Poll::Ready(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user