layout.rs 9.6 KB

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