layout.rs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. use std::sync::{Arc, Mutex};
  2. use dioxus_native_core::layout_attributes::{
  3. apply_layout_attributes_cfg, BorderWidths, LayoutConfigeration,
  4. };
  5. use dioxus_native_core::node::OwnedAttributeView;
  6. use dioxus_native_core::node_ref::{AttributeMask, NodeMask, NodeView};
  7. use dioxus_native_core::{Pass, SendAnyMap};
  8. use dioxus_native_core_macro::sorted_str_slice;
  9. use taffy::prelude::*;
  10. use crate::{screen_to_layout_space, unit_to_layout_space};
  11. #[derive(Debug, Clone, Copy, PartialEq)]
  12. pub(crate) enum PossiblyUninitalized<T> {
  13. Uninitalized,
  14. Initialized(T),
  15. }
  16. impl<T> PossiblyUninitalized<T> {
  17. pub fn unwrap(self) -> T {
  18. match self {
  19. Self::Initialized(i) => i,
  20. _ => panic!("uninitalized"),
  21. }
  22. }
  23. pub fn ok(self) -> Option<T> {
  24. match self {
  25. Self::Initialized(i) => Some(i),
  26. _ => None,
  27. }
  28. }
  29. }
  30. impl<T> Default for PossiblyUninitalized<T> {
  31. fn default() -> Self {
  32. Self::Uninitalized
  33. }
  34. }
  35. #[derive(Clone, PartialEq, Default, Debug)]
  36. pub(crate) struct TaffyLayout {
  37. pub style: Style,
  38. pub node: PossiblyUninitalized<Node>,
  39. }
  40. impl Pass for TaffyLayout {
  41. type ChildDependencies = (Self,);
  42. type ParentDependencies = ();
  43. type NodeDependencies = ();
  44. const NODE_MASK: NodeMask =
  45. NodeMask::new_with_attrs(AttributeMask::Static(SORTED_LAYOUT_ATTRS)).with_text();
  46. fn pass<'a>(
  47. &mut self,
  48. node_view: NodeView,
  49. _: <Self::NodeDependencies as dioxus_native_core::Dependancy>::ElementBorrowed<'a>,
  50. _: Option<
  51. <Self::ParentDependencies as dioxus_native_core::Dependancy>::ElementBorrowed<'a>,
  52. >,
  53. children: Option<
  54. impl Iterator<
  55. Item = <Self::ChildDependencies as dioxus_native_core::Dependancy>::ElementBorrowed<
  56. 'a,
  57. >,
  58. >,
  59. >,
  60. ctx: &SendAnyMap,
  61. ) -> bool {
  62. let mut changed = false;
  63. let taffy: &Arc<Mutex<Taffy>> = ctx.get().unwrap();
  64. let mut taffy = taffy.lock().expect("poisoned taffy");
  65. let mut style = Style::default();
  66. if let Some(text) = node_view.text() {
  67. let char_len = text.chars().count();
  68. style = Style {
  69. size: Size {
  70. // characters are 1 point tall
  71. height: Dimension::Points(screen_to_layout_space(1)),
  72. // text is as long as it is declared
  73. width: Dimension::Points(screen_to_layout_space(char_len as u16)),
  74. },
  75. ..Default::default()
  76. };
  77. if let PossiblyUninitalized::Initialized(n) = self.node {
  78. if self.style != style {
  79. taffy.set_style(n, style).unwrap();
  80. }
  81. } else {
  82. self.node = PossiblyUninitalized::Initialized(taffy.new_leaf(style).unwrap());
  83. changed = true;
  84. }
  85. } else {
  86. // gather up all the styles from the attribute list
  87. if let Some(attributes) = node_view.attributes() {
  88. for OwnedAttributeView {
  89. attribute, value, ..
  90. } in attributes
  91. {
  92. assert!(SORTED_LAYOUT_ATTRS
  93. .binary_search(&attribute.name.as_ref())
  94. .is_ok());
  95. if let Some(text) = value.as_text() {
  96. apply_layout_attributes_cfg(
  97. &attribute.name,
  98. text,
  99. &mut style,
  100. &LayoutConfigeration {
  101. border_widths: BorderWidths {
  102. thin: 1.0,
  103. medium: 1.0,
  104. thick: 1.0,
  105. },
  106. },
  107. );
  108. }
  109. }
  110. }
  111. // Set all direct nodes as our children
  112. let mut child_layout = vec![];
  113. if let Some(children) = children {
  114. for (l,) in children {
  115. child_layout.push(l.node.unwrap());
  116. }
  117. }
  118. fn scale_dimention(d: Dimension) -> Dimension {
  119. match d {
  120. Dimension::Points(p) => Dimension::Points(unit_to_layout_space(p)),
  121. Dimension::Percent(p) => Dimension::Percent(p),
  122. Dimension::Auto => Dimension::Auto,
  123. Dimension::Undefined => Dimension::Undefined,
  124. }
  125. }
  126. let style = Style {
  127. position: Rect {
  128. left: scale_dimention(style.position.left),
  129. right: scale_dimention(style.position.right),
  130. top: scale_dimention(style.position.top),
  131. bottom: scale_dimention(style.position.bottom),
  132. },
  133. margin: Rect {
  134. left: scale_dimention(style.margin.left),
  135. right: scale_dimention(style.margin.right),
  136. top: scale_dimention(style.margin.top),
  137. bottom: scale_dimention(style.margin.bottom),
  138. },
  139. padding: Rect {
  140. left: scale_dimention(style.padding.left),
  141. right: scale_dimention(style.padding.right),
  142. top: scale_dimention(style.padding.top),
  143. bottom: scale_dimention(style.padding.bottom),
  144. },
  145. border: Rect {
  146. left: scale_dimention(style.border.left),
  147. right: scale_dimention(style.border.right),
  148. top: scale_dimention(style.border.top),
  149. bottom: scale_dimention(style.border.bottom),
  150. },
  151. gap: Size {
  152. width: scale_dimention(style.gap.width),
  153. height: scale_dimention(style.gap.height),
  154. },
  155. flex_basis: scale_dimention(style.flex_basis),
  156. size: Size {
  157. width: scale_dimention(style.size.width),
  158. height: scale_dimention(style.size.height),
  159. },
  160. min_size: Size {
  161. width: scale_dimention(style.min_size.width),
  162. height: scale_dimention(style.min_size.height),
  163. },
  164. max_size: Size {
  165. width: scale_dimention(style.max_size.width),
  166. height: scale_dimention(style.max_size.height),
  167. },
  168. ..style
  169. };
  170. if let PossiblyUninitalized::Initialized(n) = self.node {
  171. if self.style != style {
  172. taffy.set_style(n, style).unwrap();
  173. }
  174. if taffy.children(n).unwrap() != child_layout {
  175. taffy.set_children(n, &child_layout).unwrap();
  176. }
  177. } else {
  178. self.node = PossiblyUninitalized::Initialized(
  179. taffy.new_with_children(style, &child_layout).unwrap(),
  180. );
  181. changed = true;
  182. }
  183. }
  184. if self.style != style {
  185. changed = true;
  186. self.style = style;
  187. }
  188. changed
  189. }
  190. }
  191. // these are the attributes in layout_attiributes in native-core
  192. const SORTED_LAYOUT_ATTRS: &[&str] = &sorted_str_slice!([
  193. "align-content",
  194. "align-items",
  195. "align-self",
  196. "animation",
  197. "animation-delay",
  198. "animation-direction",
  199. "animation-duration",
  200. "animation-fill-mode",
  201. "animation-iteration-count",
  202. "animation-name",
  203. "animation-play-state",
  204. "animation-timing-function",
  205. "backface-visibility",
  206. "border",
  207. "border-bottom",
  208. "border-bottom-color",
  209. "border-bottom-left-radius",
  210. "border-bottom-right-radius",
  211. "border-bottom-style",
  212. "border-bottom-width",
  213. "border-collapse",
  214. "border-color",
  215. "border-image",
  216. "border-image-outset",
  217. "border-image-repeat",
  218. "border-image-slice",
  219. "border-image-source",
  220. "border-image-width",
  221. "border-left",
  222. "border-left-color",
  223. "border-left-style",
  224. "border-left-width",
  225. "border-radius",
  226. "border-right",
  227. "border-right-color",
  228. "border-right-style",
  229. "border-right-width",
  230. "border-spacing",
  231. "border-style",
  232. "border-top",
  233. "border-top-color",
  234. "border-top-left-radius",
  235. "border-top-right-radius",
  236. "border-top-style",
  237. "border-top-width",
  238. "border-width",
  239. "bottom",
  240. "box-shadow",
  241. "box-sizing",
  242. "caption-side",
  243. "clear",
  244. "clip",
  245. "column-count",
  246. "column-fill",
  247. "column-gap",
  248. "column-rule",
  249. "column-rule-color",
  250. "column-rule-style",
  251. "column-rule-width",
  252. "column-span",
  253. "column-width",
  254. "columns",
  255. "content",
  256. "counter-increment",
  257. "counter-reset",
  258. "cursor",
  259. "direction",
  260. "ltr",
  261. "rtl",
  262. "display",
  263. "empty-cells",
  264. "flex",
  265. "flex-basis",
  266. "flex-direction",
  267. "flex-flow",
  268. "flex-grow",
  269. "flex-shrink",
  270. "flex-wrap",
  271. "float",
  272. "height",
  273. "justify-content",
  274. "flex-start",
  275. "flex-end",
  276. "center",
  277. "space-between",
  278. "space-around",
  279. "space-evenly",
  280. "left",
  281. "letter-spacing",
  282. "line-height",
  283. "list-style",
  284. "list-style-image",
  285. "list-style-position",
  286. "list-style-type",
  287. "margin",
  288. "margin-bottom",
  289. "margin-left",
  290. "margin-right",
  291. "margin-top",
  292. "max-height",
  293. "max-width",
  294. "min-height",
  295. "min-width",
  296. "opacity",
  297. "order",
  298. "outline",
  299. "outline-color",
  300. "outline-offset",
  301. "outline-style",
  302. "outline-width",
  303. "overflow",
  304. "overflow-x",
  305. "overflow-y",
  306. "padding",
  307. "padding-bottom",
  308. "padding-left",
  309. "padding-right",
  310. "padding-top",
  311. "page-break-after",
  312. "page-break-before",
  313. "page-break-inside",
  314. "perspective",
  315. "perspective-origin",
  316. "position",
  317. "static",
  318. "relative",
  319. "fixed",
  320. "absolute",
  321. "sticky",
  322. "pointer-events",
  323. "quotes",
  324. "resize",
  325. "right",
  326. "tab-size",
  327. "table-layout",
  328. "top",
  329. "transform",
  330. "transform-origin",
  331. "transform-style",
  332. "transition",
  333. "transition-delay",
  334. "transition-duration",
  335. "transition-property",
  336. "transition-timing-function",
  337. "vertical-align",
  338. "visibility",
  339. "white-space",
  340. "width",
  341. "word-break",
  342. "word-spacing",
  343. "word-wrap",
  344. "z-index"
  345. ]);