layout.rs 12 KB

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