mutations.rs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  1. use crate::{arena::ElementId, AttributeValue, Template};
  2. /// Something that can handle the mutations that are generated by the diffing process and apply them to the Real DOM
  3. ///
  4. /// This object provides a bunch of important information for a renderer to use patch the Real Dom with the state of the
  5. /// VirtualDom. This includes the scopes that were modified, the templates that were discovered, and a list of changes
  6. /// in the form of a [`Mutation`].
  7. ///
  8. /// These changes are specific to one subtree, so to patch multiple subtrees, you'd need to handle each set separately.
  9. ///
  10. /// Templates, however, apply to all subtrees, not just target subtree.
  11. ///
  12. /// Mutations are the only link between the RealDOM and the VirtualDOM.
  13. pub trait WriteMutations {
  14. /// Add these m children to the target element
  15. ///
  16. /// Id: The ID of the element being mounted to
  17. /// M: The number of nodes on the stack to append to the target element
  18. fn append_children(&mut self, id: ElementId, m: usize);
  19. /// Assign the element at the given path the target ElementId.
  20. ///
  21. /// The path is in the form of a list of indices based on children. Templates cannot have more than 255 children per
  22. /// element, hence the use of a single byte.
  23. ///
  24. /// Path: The path of the child of the topmost node on the stack. A path of `[]` represents the topmost node. A path of `[0]` represents the first child. `[0,1,2]` represents 1st child's 2nd child's 3rd child.
  25. /// Id: The ID we're assigning to this element/placeholder. This will be used later to modify the element or replace it with another element.
  26. fn assign_node_id(&mut self, path: &'static [u8], id: ElementId);
  27. /// Create a placeholder in the DOM that we will use later.
  28. ///
  29. /// Dioxus currently requires the use of placeholders to maintain a re-entrance point for things like list diffing
  30. ///
  31. /// Id: The ID we're assigning to this element/placeholder. This will be used later to modify the element or replace it with another element.
  32. fn create_placeholder(&mut self, id: ElementId);
  33. /// Create a node specifically for text with the given value
  34. ///
  35. /// Value: The text content of this text node
  36. /// Id: The ID we're assigning to this specific text nodes. This will be used later to modify the element or replace it with another element.
  37. fn create_text_node(&mut self, value: &str, id: ElementId);
  38. /// Load and clone an existing node from a template saved under that specific name
  39. ///
  40. /// Dioxus guarantees that the renderer will have already been provided the template.
  41. /// When the template is picked up in the template list, it should be saved under its "name" - here, the name
  42. ///
  43. /// Name: The unique "name" of the template based on the template location. When paired with `rsx!`, this is autogenerated
  44. /// Index: The index root we loading from the template. The template is stored as a list of nodes. This index represents the position of that root
  45. /// Id: The ID we're assigning to this element being loaded from the template (This will be used later to move the element around in lists)
  46. fn load_template(&mut self, template: Template, index: usize, id: ElementId);
  47. /// Replace the target element (given by its ID) with the topmost m nodes on the stack
  48. ///
  49. /// id: The ID of the node we're going to replace with new nodes
  50. /// m: The number of nodes on the stack to replace the target element with
  51. fn replace_node_with(&mut self, id: ElementId, m: usize);
  52. /// Replace an existing element in the template at the given path with the m nodes on the stack
  53. ///
  54. /// Path: The path of the child of the topmost node on the stack. A path of `[]` represents the topmost node. A path of `[0]` represents the first child. `[0,1,2]` represents 1st child's 2nd child's 3rd child.
  55. /// M: The number of nodes on the stack to replace the target element with
  56. fn replace_placeholder_with_nodes(&mut self, path: &'static [u8], m: usize);
  57. /// Insert a number of nodes after a given node.
  58. ///
  59. /// Id: The ID of the node to insert after.
  60. /// M: The number of nodes on the stack to insert after the target node.
  61. fn insert_nodes_after(&mut self, id: ElementId, m: usize);
  62. /// Insert a number of nodes before a given node.
  63. ///
  64. /// Id: The ID of the node to insert before.
  65. /// M: The number of nodes on the stack to insert before the target node.
  66. fn insert_nodes_before(&mut self, id: ElementId, m: usize);
  67. /// Set the value of a node's attribute.
  68. ///
  69. /// Name: The name of the attribute to set.
  70. /// NS: The (optional) namespace of the attribute. For instance, "style" is in the "style" namespace.
  71. /// Value: The value of the attribute.
  72. /// Id: The ID of the node to set the attribute of.
  73. fn set_attribute(
  74. &mut self,
  75. name: &'static str,
  76. ns: Option<&'static str>,
  77. value: &AttributeValue,
  78. id: ElementId,
  79. );
  80. /// Set the text content of a node.
  81. ///
  82. /// Value: The textcontent of the node
  83. /// Id: The ID of the node to set the textcontent of.
  84. fn set_node_text(&mut self, value: &str, id: ElementId);
  85. /// Create a new Event Listener.
  86. ///
  87. /// Name: The name of the event to listen for.
  88. /// Id: The ID of the node to attach the listener to.
  89. fn create_event_listener(&mut self, name: &'static str, id: ElementId);
  90. /// Remove an existing Event Listener.
  91. ///
  92. /// Name: The name of the event to remove.
  93. /// Id: The ID of the node to remove.
  94. fn remove_event_listener(&mut self, name: &'static str, id: ElementId);
  95. /// Remove a particular node from the DOM
  96. ///
  97. /// Id: The ID of the node to remove.
  98. fn remove_node(&mut self, id: ElementId);
  99. /// Push the given root node onto our stack.
  100. ///
  101. /// Id: The ID of the root node to push.
  102. fn push_root(&mut self, id: ElementId);
  103. }
  104. /// A `Mutation` represents a single instruction for the renderer to use to modify the UI tree to match the state
  105. /// of the Dioxus VirtualDom.
  106. ///
  107. /// These edits can be serialized and sent over the network or through any interface
  108. #[derive(Debug, PartialEq)]
  109. pub enum Mutation {
  110. /// Add these m children to the target element
  111. AppendChildren {
  112. /// The ID of the element being mounted to
  113. id: ElementId,
  114. /// The number of nodes on the stack to append to the target element
  115. m: usize,
  116. },
  117. /// Assign the element at the given path the target ElementId.
  118. ///
  119. /// The path is in the form of a list of indices based on children. Templates cannot have more than 255 children per
  120. /// element, hence the use of a single byte.
  121. AssignId {
  122. /// The path of the child of the topmost node on the stack
  123. ///
  124. /// A path of `[]` represents the topmost node. A path of `[0]` represents the first child.
  125. /// `[0,1,2]` represents 1st child's 2nd child's 3rd child.
  126. path: &'static [u8],
  127. /// The ID we're assigning to this element/placeholder.
  128. ///
  129. /// This will be used later to modify the element or replace it with another element.
  130. id: ElementId,
  131. },
  132. /// Create a placeholder in the DOM that we will use later.
  133. ///
  134. /// Dioxus currently requires the use of placeholders to maintain a re-entrance point for things like list diffing
  135. CreatePlaceholder {
  136. /// The ID we're assigning to this element/placeholder.
  137. ///
  138. /// This will be used later to modify the element or replace it with another element.
  139. id: ElementId,
  140. },
  141. /// Create a node specifically for text with the given value
  142. CreateTextNode {
  143. /// The text content of this text node
  144. value: String,
  145. /// The ID we're assigning to this specific text nodes
  146. ///
  147. /// This will be used later to modify the element or replace it with another element.
  148. id: ElementId,
  149. },
  150. /// Load and clone an existing node from a template with a given ID
  151. ///
  152. /// Dioxus guarantees that the renderer will have already been provided the template.
  153. /// When the template is picked up in the template list, it should be saved under its "name" - here, the name
  154. LoadTemplate {
  155. /// Which root are we loading from the template?
  156. ///
  157. /// The template is stored as a list of nodes. This index represents the position of that root
  158. index: usize,
  159. /// The ID we're assigning to this element being loaded from the template
  160. ///
  161. /// This will be used later to move the element around in lists
  162. id: ElementId,
  163. },
  164. /// Replace the target element (given by its ID) with the topmost m nodes on the stack
  165. ReplaceWith {
  166. /// The ID of the node we're going to replace with
  167. id: ElementId,
  168. /// The number of nodes on the stack to replace the target element with
  169. m: usize,
  170. },
  171. /// Replace an existing element in the template at the given path with the m nodes on the stack
  172. ReplacePlaceholder {
  173. /// The path of the child of the topmost node on the stack
  174. ///
  175. /// A path of `[]` represents the topmost node. A path of `[0]` represents the first child.
  176. /// `[0,1,2]` represents 1st child's 2nd child's 3rd child.
  177. path: &'static [u8],
  178. /// The number of nodes on the stack to replace the target element with
  179. m: usize,
  180. },
  181. /// Insert a number of nodes after a given node.
  182. InsertAfter {
  183. /// The ID of the node to insert after.
  184. id: ElementId,
  185. /// The number of nodes on the stack to insert after the target node.
  186. m: usize,
  187. },
  188. /// Insert a number of nodes before a given node.
  189. InsertBefore {
  190. /// The ID of the node to insert before.
  191. id: ElementId,
  192. /// The number of nodes on the stack to insert before the target node.
  193. m: usize,
  194. },
  195. /// Set the value of a node's attribute.
  196. SetAttribute {
  197. /// The name of the attribute to set.
  198. name: &'static str,
  199. /// The (optional) namespace of the attribute.
  200. /// For instance, "style" is in the "style" namespace.
  201. ns: Option<&'static str>,
  202. /// The value of the attribute.
  203. value: AttributeValue,
  204. /// The ID of the node to set the attribute of.
  205. id: ElementId,
  206. },
  207. /// Set the textcontent of a node.
  208. SetText {
  209. /// The textcontent of the node
  210. value: String,
  211. /// The ID of the node to set the textcontent of.
  212. id: ElementId,
  213. },
  214. /// Create a new Event Listener.
  215. NewEventListener {
  216. /// The name of the event to listen for.
  217. name: String,
  218. /// The ID of the node to attach the listener to.
  219. id: ElementId,
  220. },
  221. /// Remove an existing Event Listener.
  222. RemoveEventListener {
  223. /// The name of the event to remove.
  224. name: String,
  225. /// The ID of the node to remove.
  226. id: ElementId,
  227. },
  228. /// Remove a particular node from the DOM
  229. Remove {
  230. /// The ID of the node to remove.
  231. id: ElementId,
  232. },
  233. /// Push the given root node onto our stack.
  234. PushRoot {
  235. /// The ID of the root node to push.
  236. id: ElementId,
  237. },
  238. }
  239. /// A static list of mutations that can be applied to the DOM. Note: this list does not contain any `Any` attribute values
  240. #[derive(Debug, PartialEq, Default)]
  241. pub struct Mutations {
  242. /// Any mutations required to patch the renderer to match the layout of the VirtualDom
  243. pub edits: Vec<Mutation>,
  244. }
  245. impl WriteMutations for Mutations {
  246. fn append_children(&mut self, id: ElementId, m: usize) {
  247. self.edits.push(Mutation::AppendChildren { id, m })
  248. }
  249. fn assign_node_id(&mut self, path: &'static [u8], id: ElementId) {
  250. self.edits.push(Mutation::AssignId { path, id })
  251. }
  252. fn create_placeholder(&mut self, id: ElementId) {
  253. self.edits.push(Mutation::CreatePlaceholder { id })
  254. }
  255. fn create_text_node(&mut self, value: &str, id: ElementId) {
  256. self.edits.push(Mutation::CreateTextNode {
  257. value: value.into(),
  258. id,
  259. })
  260. }
  261. fn load_template(&mut self, _template: Template, index: usize, id: ElementId) {
  262. self.edits.push(Mutation::LoadTemplate { index, id })
  263. }
  264. fn replace_node_with(&mut self, id: ElementId, m: usize) {
  265. self.edits.push(Mutation::ReplaceWith { id, m })
  266. }
  267. fn replace_placeholder_with_nodes(&mut self, path: &'static [u8], m: usize) {
  268. self.edits.push(Mutation::ReplacePlaceholder { path, m })
  269. }
  270. fn insert_nodes_after(&mut self, id: ElementId, m: usize) {
  271. self.edits.push(Mutation::InsertAfter { id, m })
  272. }
  273. fn insert_nodes_before(&mut self, id: ElementId, m: usize) {
  274. self.edits.push(Mutation::InsertBefore { id, m })
  275. }
  276. fn set_attribute(
  277. &mut self,
  278. name: &'static str,
  279. ns: Option<&'static str>,
  280. value: &AttributeValue,
  281. id: ElementId,
  282. ) {
  283. self.edits.push(Mutation::SetAttribute {
  284. name,
  285. ns,
  286. value: match value {
  287. AttributeValue::Text(s) => AttributeValue::Text(s.clone()),
  288. AttributeValue::Bool(b) => AttributeValue::Bool(*b),
  289. AttributeValue::Float(n) => AttributeValue::Float(*n),
  290. AttributeValue::Int(n) => AttributeValue::Int(*n),
  291. AttributeValue::None => AttributeValue::None,
  292. _ => panic!("Cannot serialize attribute value"),
  293. },
  294. id,
  295. })
  296. }
  297. fn set_node_text(&mut self, value: &str, id: ElementId) {
  298. self.edits.push(Mutation::SetText {
  299. value: value.into(),
  300. id,
  301. })
  302. }
  303. fn create_event_listener(&mut self, name: &'static str, id: ElementId) {
  304. self.edits.push(Mutation::NewEventListener {
  305. name: name.into(),
  306. id,
  307. })
  308. }
  309. fn remove_event_listener(&mut self, name: &'static str, id: ElementId) {
  310. self.edits.push(Mutation::RemoveEventListener {
  311. name: name.into(),
  312. id,
  313. })
  314. }
  315. fn remove_node(&mut self, id: ElementId) {
  316. self.edits.push(Mutation::Remove { id })
  317. }
  318. fn push_root(&mut self, id: ElementId) {
  319. self.edits.push(Mutation::PushRoot { id })
  320. }
  321. }
  322. /// A struct that ignores all mutations
  323. pub struct NoOpMutations;
  324. impl WriteMutations for NoOpMutations {
  325. fn append_children(&mut self, _: ElementId, _: usize) {}
  326. fn assign_node_id(&mut self, _: &'static [u8], _: ElementId) {}
  327. fn create_placeholder(&mut self, _: ElementId) {}
  328. fn create_text_node(&mut self, _: &str, _: ElementId) {}
  329. fn load_template(&mut self, _: Template, _: usize, _: ElementId) {}
  330. fn replace_node_with(&mut self, _: ElementId, _: usize) {}
  331. fn replace_placeholder_with_nodes(&mut self, _: &'static [u8], _: usize) {}
  332. fn insert_nodes_after(&mut self, _: ElementId, _: usize) {}
  333. fn insert_nodes_before(&mut self, _: ElementId, _: usize) {}
  334. fn set_attribute(
  335. &mut self,
  336. _: &'static str,
  337. _: Option<&'static str>,
  338. _: &AttributeValue,
  339. _: ElementId,
  340. ) {
  341. }
  342. fn set_node_text(&mut self, _: &str, _: ElementId) {}
  343. fn create_event_listener(&mut self, _: &'static str, _: ElementId) {}
  344. fn remove_event_listener(&mut self, _: &'static str, _: ElementId) {}
  345. fn remove_node(&mut self, _: ElementId) {}
  346. fn push_root(&mut self, _: ElementId) {}
  347. }