dioxus/packages/core/src/mutations.rs

251 lines
6.5 KiB
Rust
Raw Normal View History

2021-08-23 14:43:49 +00:00
//! Instructions returned by the VirtualDOM on how to modify the Real DOM.
//!
//! This module contains an internal API to generate these instructions.
//!
//! Beware that changing code in this module will break compatibility with
//! interpreters for these types of DomEdits.
2021-08-22 21:08:25 +00:00
use crate::innerlude::*;
2021-11-10 22:09:52 +00:00
use std::{any::Any, fmt::Debug};
2021-08-22 21:08:25 +00:00
2021-11-15 14:49:01 +00:00
/// ## Mutations
///
/// This method returns "mutations" - IE the necessary changes to get the RealDOM to match the VirtualDOM. It also
/// includes a list of NodeRefs that need to be applied and effects that need to be triggered after the RealDOM has
/// applied the edits.
///
/// Mutations are the only link between the RealDOM and the VirtualDOM.
2021-08-23 14:43:49 +00:00
pub struct Mutations<'a> {
pub edits: Vec<DomEdit<'a>>,
2021-12-01 03:48:05 +00:00
pub dirty_scopes: FxHashSet<ScopeId>,
2021-11-15 14:49:01 +00:00
pub refs: Vec<NodeRefMutation<'a>>,
2021-11-10 22:09:52 +00:00
}
2021-11-15 14:49:01 +00:00
2021-11-10 22:09:52 +00:00
impl Debug for Mutations<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Mutations")
.field("edits", &self.edits)
2021-11-15 14:49:01 +00:00
.field("noderefs", &self.refs)
2021-11-10 22:09:52 +00:00
.finish()
}
2021-08-22 21:08:25 +00:00
}
2021-08-23 14:43:49 +00:00
2021-11-15 14:49:01 +00:00
/// A `DomEdit` represents a serialized form of the VirtualDom's trait-based API. This allows streaming edits across the
/// network or through FFI boundaries.
#[derive(Debug, PartialEq)]
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
serde(tag = "type")
)]
pub enum DomEdit<'bump> {
PushRoot {
root: u64,
},
PopRoot,
AppendChildren {
many: u32,
},
2021-12-21 03:33:13 +00:00
// // save a possibly-fragment node as a template
// SaveAsTemplate {
// many: u32,
// },
2021-11-15 14:49:01 +00:00
// "Root" refers to the item directly
// it's a waste of an instruction to push the root directly
ReplaceWith {
root: u64,
m: u32,
},
InsertAfter {
root: u64,
n: u32,
},
InsertBefore {
root: u64,
n: u32,
},
Remove {
root: u64,
},
CreateTextNode {
text: &'bump str,
root: u64,
},
CreateElement {
tag: &'bump str,
root: u64,
},
CreateElementNs {
tag: &'bump str,
root: u64,
ns: &'static str,
},
CreatePlaceholder {
root: u64,
},
NewEventListener {
event_name: &'static str,
scope: ScopeId,
root: u64,
},
RemoveEventListener {
root: u64,
event: &'static str,
},
SetText {
root: u64,
text: &'bump str,
},
SetAttribute {
root: u64,
field: &'static str,
value: &'bump str,
ns: Option<&'bump str>,
},
RemoveAttribute {
root: u64,
name: &'static str,
},
}
2021-12-01 03:48:05 +00:00
use fxhash::FxHashSet;
2021-08-22 21:08:25 +00:00
use DomEdit::*;
2021-08-23 14:43:49 +00:00
impl<'a> Mutations<'a> {
2021-08-26 21:05:28 +00:00
pub(crate) fn new() -> Self {
2021-11-10 22:09:52 +00:00
Self {
edits: Vec::new(),
2021-11-15 14:49:01 +00:00
refs: Vec::new(),
2021-12-01 03:48:05 +00:00
dirty_scopes: Default::default(),
2021-11-10 22:09:52 +00:00
}
2021-08-22 21:08:25 +00:00
}
// Navigation
pub(crate) fn push_root(&mut self, root: ElementId) {
let id = root.as_u64();
2021-10-05 07:37:15 +00:00
self.edits.push(PushRoot { root: id });
2021-08-22 21:08:25 +00:00
}
2021-08-23 14:43:49 +00:00
pub(crate) fn replace_with(&mut self, root: ElementId, m: u32) {
let root = root.as_u64();
self.edits.push(ReplaceWith { m, root });
2021-08-22 21:08:25 +00:00
}
2021-08-23 14:43:49 +00:00
pub(crate) fn insert_after(&mut self, root: ElementId, n: u32) {
let root = root.as_u64();
self.edits.push(InsertAfter { n, root });
2021-08-22 21:08:25 +00:00
}
2021-08-23 14:43:49 +00:00
pub(crate) fn insert_before(&mut self, root: ElementId, n: u32) {
let root = root.as_u64();
self.edits.push(InsertBefore { n, root });
2021-08-22 21:08:25 +00:00
}
2021-08-23 14:43:49 +00:00
2021-10-24 17:30:36 +00:00
// Remove Nodes from the dom
2021-08-23 14:43:49 +00:00
pub(crate) fn remove(&mut self, id: u64) {
self.edits.push(Remove { root: id });
2021-08-22 21:08:25 +00:00
}
2021-08-23 14:43:49 +00:00
2021-08-22 21:08:25 +00:00
// Create
2021-08-23 14:43:49 +00:00
pub(crate) fn create_text_node(&mut self, text: &'a str, id: ElementId) {
2021-08-22 21:08:25 +00:00
let id = id.as_u64();
2021-10-05 07:37:15 +00:00
self.edits.push(CreateTextNode { text, root: id });
2021-08-22 21:08:25 +00:00
}
2021-08-23 14:43:49 +00:00
2021-08-22 21:08:25 +00:00
pub(crate) fn create_element(
&mut self,
tag: &'static str,
ns: Option<&'static str>,
id: ElementId,
) {
let id = id.as_u64();
match ns {
2021-10-05 07:37:15 +00:00
Some(ns) => self.edits.push(CreateElementNs { root: id, ns, tag }),
None => self.edits.push(CreateElement { root: id, tag }),
2021-08-22 21:08:25 +00:00
}
}
// placeholders are nodes that don't get rendered but still exist as an "anchor" in the real dom
pub(crate) fn create_placeholder(&mut self, id: ElementId) {
let id = id.as_u64();
2021-10-05 07:37:15 +00:00
self.edits.push(CreatePlaceholder { root: id });
2021-08-22 21:08:25 +00:00
}
2021-08-23 14:43:49 +00:00
2021-08-22 21:08:25 +00:00
// events
pub(crate) fn new_event_listener(&mut self, listener: &Listener, scope: ScopeId) {
let Listener {
event,
mounted_node,
..
} = listener;
let element_id = mounted_node.get().unwrap().as_u64();
self.edits.push(NewEventListener {
scope,
event_name: event,
2021-10-05 07:37:15 +00:00
root: element_id,
2021-08-22 21:08:25 +00:00
});
}
2021-10-11 19:35:20 +00:00
pub(crate) fn remove_event_listener(&mut self, event: &'static str, root: u64) {
self.edits.push(RemoveEventListener { event, root });
2021-08-22 21:08:25 +00:00
}
2021-08-23 14:43:49 +00:00
2021-08-22 21:08:25 +00:00
// modify
2021-10-11 19:35:20 +00:00
pub(crate) fn set_text(&mut self, text: &'a str, root: u64) {
self.edits.push(SetText { text, root });
2021-08-22 21:08:25 +00:00
}
2021-08-23 14:43:49 +00:00
2021-10-11 19:35:20 +00:00
pub(crate) fn set_attribute(&mut self, attribute: &'a Attribute, root: u64) {
2021-08-22 21:08:25 +00:00
let Attribute {
name,
value,
namespace,
..
} = attribute;
self.edits.push(SetAttribute {
field: name,
value,
ns: *namespace,
2021-10-11 19:35:20 +00:00
root,
2021-08-22 21:08:25 +00:00
});
}
2021-08-23 14:43:49 +00:00
2021-10-11 19:35:20 +00:00
pub(crate) fn remove_attribute(&mut self, attribute: &Attribute, root: u64) {
2021-08-22 21:08:25 +00:00
let name = attribute.name;
2021-10-11 19:35:20 +00:00
self.edits.push(RemoveAttribute { name, root });
2021-08-22 21:08:25 +00:00
}
}
// refs are only assigned once
pub struct NodeRefMutation<'a> {
2021-08-24 19:12:20 +00:00
pub element: &'a mut Option<once_cell::sync::OnceCell<Box<dyn Any>>>,
pub element_id: ElementId,
2021-08-22 21:08:25 +00:00
}
impl<'a> std::fmt::Debug for NodeRefMutation<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("NodeRefMutation")
.field("element_id", &self.element_id)
.finish()
}
}
impl<'a> NodeRefMutation<'a> {
pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
self.element
.as_ref()
.and_then(|f| f.get())
.and_then(|f| f.downcast_ref::<T>())
}
pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
self.element
.as_mut()
.and_then(|f| f.get_mut())
.and_then(|f| f.downcast_mut::<T>())
}
}