layout.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. use std::{ops::Deref, rc::Rc};
  2. use cache::{Layer, LayoutCacheKey, NodeState};
  3. use kahlo::math::{PixelBox, PixelSideOffsets};
  4. mod arr;
  5. mod cache;
  6. mod calc;
  7. pub use cache::LayoutCache;
  8. pub use calc::recalculate;
  9. /// Sizing policy for a layout dimension. Defaults to no minimum or desired size and a low-weighted
  10. /// desire to take up slack space.
  11. #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
  12. pub struct SizePolicy {
  13. /// The minimum number of pixels required along this dimension to avoid overlap and other
  14. /// issues.
  15. pub minimum: usize,
  16. /// The number of pixels requested to have a comfortable amount of space and avoid ugly-looking
  17. /// UI.
  18. pub desired: usize,
  19. /// How much to take up any remaining slack space.
  20. pub slack_weight: usize,
  21. }
  22. impl Default for SizePolicy {
  23. fn default() -> Self {
  24. Self {
  25. minimum: 0,
  26. desired: 0,
  27. slack_weight: 1,
  28. }
  29. }
  30. }
  31. impl SizePolicy {
  32. pub fn expands(weight: usize) -> Self {
  33. Self {
  34. minimum: 0,
  35. desired: 0,
  36. slack_weight: weight,
  37. }
  38. }
  39. pub fn max(self, rhs: SizePolicy) -> SizePolicy {
  40. Self {
  41. minimum: self.minimum.max(rhs.minimum),
  42. desired: self.desired.max(rhs.desired),
  43. slack_weight: self.slack_weight.max(rhs.slack_weight),
  44. }
  45. }
  46. pub fn max_preserve_slack(self, rhs: SizePolicy) -> SizePolicy {
  47. Self {
  48. minimum: self.minimum.max(rhs.minimum),
  49. desired: self.desired.max(rhs.desired),
  50. slack_weight: self.slack_weight,
  51. }
  52. }
  53. }
  54. impl std::ops::Add<Self> for SizePolicy {
  55. type Output = Self;
  56. fn add(self, rhs: Self) -> Self::Output {
  57. Self {
  58. minimum: self.minimum + rhs.minimum,
  59. desired: self.desired + rhs.desired,
  60. slack_weight: self.slack_weight + rhs.slack_weight,
  61. }
  62. }
  63. }
  64. /// How to horizontally align a smaller node inside a larger node.
  65. #[derive(Clone, Copy, PartialEq, Debug)]
  66. pub enum HorizontalAlignment {
  67. Left,
  68. Centre,
  69. Right,
  70. }
  71. impl Default for HorizontalAlignment {
  72. fn default() -> Self {
  73. Self::Centre
  74. }
  75. }
  76. #[derive(Clone, Copy, PartialEq, Debug)]
  77. pub enum VerticalAlignment {
  78. Top,
  79. Centre,
  80. Bottom,
  81. }
  82. impl Default for VerticalAlignment {
  83. fn default() -> Self {
  84. Self::Centre
  85. }
  86. }
  87. /// What sort of layout does a node represent?
  88. #[derive(Clone, Copy, PartialEq, Debug)]
  89. pub enum NodeBehaviour {
  90. /// A fixed rendering area, probably a root node.
  91. Fixed { area: PixelBox },
  92. /// An ordinary box sitting inside another node, hinting its size and receiving a final
  93. /// size assignment from its parent.
  94. Element,
  95. /// A node that floats above other content, anchored with a zero-size flexbox.
  96. Anchored,
  97. }
  98. #[derive(Clone)]
  99. pub enum ChildArrangement {
  100. Custom(std::rc::Rc<dyn arr::ArrangementCalculator>),
  101. Column,
  102. Row,
  103. Table,
  104. }
  105. impl std::fmt::Debug for ChildArrangement {
  106. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  107. match self {
  108. Self::Custom(_) => f.write_str("ChildArrangement::Custom"),
  109. Self::Column => f.write_str("ChildArrangement::Column"),
  110. Self::Row => f.write_str("ChildArrangement::Row"),
  111. Self::Table => f.write_str("ChildArrangement::Table"),
  112. }
  113. }
  114. }
  115. impl Deref for ChildArrangement {
  116. type Target = dyn arr::ArrangementCalculator;
  117. fn deref(&self) -> &Self::Target {
  118. match self {
  119. Self::Custom(calc) => calc.as_ref(),
  120. Self::Column => &arr::LineArrangement::Column,
  121. Self::Row => &arr::LineArrangement::Row,
  122. Self::Table => todo!(),
  123. }
  124. }
  125. }
  126. pub struct LayoutNode {
  127. label: Option<String>,
  128. cache_key: LayoutCacheKey,
  129. cache: Rc<LayoutCache>,
  130. behaviour: NodeBehaviour,
  131. child_arrangement: ChildArrangement,
  132. width_policy: SizePolicy,
  133. height_policy: SizePolicy,
  134. halign: HorizontalAlignment,
  135. valign: VerticalAlignment,
  136. margin: PixelSideOffsets,
  137. }
  138. impl std::fmt::Debug for LayoutNode {
  139. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  140. f.debug_struct("LayoutNode")
  141. .field("label", &self.label)
  142. .field("cache_key", &self.cache_key)
  143. .field("behaviour", &self.behaviour)
  144. .field("child_arrangement", &self.child_arrangement)
  145. .field("width_policy", &self.width_policy)
  146. .field("height_policy", &self.height_policy)
  147. .field("margin", &self.margin)
  148. .finish()
  149. }
  150. }
  151. impl LayoutNode {
  152. pub fn new(cache: Rc<LayoutCache>) -> Self {
  153. let cache_key = LayoutCacheKey::generate();
  154. cache.update_queue.borrow_mut().push(cache_key);
  155. Self {
  156. label: None,
  157. cache_key,
  158. cache,
  159. behaviour: NodeBehaviour::Element,
  160. child_arrangement: ChildArrangement::Column,
  161. width_policy: SizePolicy::default(),
  162. height_policy: SizePolicy::default(),
  163. halign: HorizontalAlignment::default(),
  164. valign: VerticalAlignment::default(),
  165. margin: PixelSideOffsets::new_all_same(0),
  166. }
  167. }
  168. pub fn relayout(&self) {
  169. self.cache.update_queue.borrow_mut().push(self.cache_key);
  170. }
  171. pub fn render_area(&self) -> Option<PixelBox> {
  172. self.cache
  173. .with_state(self.cache_key, |ns| ns.area)
  174. .flatten()
  175. .map(|pb| pb.inner_box(self.margin))
  176. }
  177. pub fn render_check(&self) -> bool {
  178. self.cache.with_state(self.cache_key, |ns| {
  179. let ret = ns.needs_render;
  180. ns.needs_render = false;
  181. ret
  182. }).unwrap_or(false)
  183. }
  184. pub fn render_needed(&self) {
  185. self.cache.render_queue.borrow_mut().push(self.cache_key);
  186. }
  187. }
  188. /// Accessors
  189. impl LayoutNode {
  190. pub fn label(&self) -> Option<&str> {
  191. self.label.as_ref().map(String::as_str)
  192. }
  193. pub fn set_label(&mut self, to: impl AsRef<str>) {
  194. self.label = Some(to.as_ref().to_string());
  195. }
  196. pub fn behaviour(&self) -> NodeBehaviour {
  197. self.behaviour
  198. }
  199. pub fn set_behaviour(&mut self, mode: NodeBehaviour) -> &mut Self {
  200. self.behaviour = mode;
  201. self.relayout();
  202. self
  203. }
  204. pub fn arrangement(&self) -> &ChildArrangement {
  205. &self.child_arrangement
  206. }
  207. pub fn set_arrangement(&mut self, arr: ChildArrangement) -> &mut Self {
  208. self.child_arrangement = arr;
  209. self.relayout();
  210. self
  211. }
  212. pub fn width_policy(&self) -> SizePolicy {
  213. self.width_policy
  214. }
  215. pub fn set_width_policy(&mut self, policy: SizePolicy) -> &mut Self {
  216. self.width_policy = policy;
  217. self.relayout();
  218. self
  219. }
  220. pub fn height_policy(&self) -> SizePolicy {
  221. self.height_policy
  222. }
  223. pub fn set_height_policy(&mut self, policy: SizePolicy) -> &mut Self {
  224. self.height_policy = policy;
  225. self.relayout();
  226. self
  227. }
  228. pub fn halign(&self) -> HorizontalAlignment {
  229. self.halign
  230. }
  231. pub fn set_halign(&mut self, halign: HorizontalAlignment) -> &mut Self {
  232. self.halign = halign;
  233. self
  234. }
  235. pub fn valign(&self) -> VerticalAlignment {
  236. self.valign
  237. }
  238. pub fn set_valign(&mut self, valign: VerticalAlignment) -> &mut Self {
  239. self.valign = valign;
  240. self
  241. }
  242. pub fn margin(&self) -> PixelSideOffsets {
  243. self.margin
  244. }
  245. pub fn margin_mut(&mut self) -> &mut PixelSideOffsets {
  246. &mut self.margin
  247. }
  248. }
  249. impl Clone for LayoutNode {
  250. fn clone(&self) -> Self {
  251. Self {
  252. label: self.label.clone(),
  253. cache_key: LayoutCacheKey::generate(),
  254. cache: self.cache.clone(),
  255. behaviour: self.behaviour,
  256. child_arrangement: self.child_arrangement.clone(),
  257. width_policy: self.width_policy,
  258. height_policy: self.height_policy,
  259. halign: self.halign,
  260. valign: self.valign,
  261. margin: self.margin,
  262. }
  263. }
  264. }
  265. impl Drop for LayoutNode {
  266. fn drop(&mut self) {
  267. self.cache.update_queue.borrow_mut().push(self.cache_key);
  268. }
  269. }
  270. fn dump_node_tree_helper(lna: LayoutNodeAccess, indent: usize, out: &mut String) {
  271. let ind = " ".repeat(indent);
  272. out.push_str(ind.as_str());
  273. out.push_str("Node (");
  274. out.push_str(match lna.label() {
  275. Some(v) => v,
  276. None => "<anon>",
  277. });
  278. out.push_str(
  279. format!(
  280. ") [wpol: {}/{}/{} hpol: {}/{}/{} behaviour: {:?}]\n",
  281. lna.width_policy.minimum,
  282. lna.width_policy.desired,
  283. lna.width_policy.slack_weight,
  284. lna.height_policy.minimum,
  285. lna.height_policy.desired,
  286. lna.height_policy.slack_weight,
  287. lna.behaviour
  288. )
  289. .as_str(),
  290. );
  291. out.push_str(ind.as_str());
  292. out.push_str(" ");
  293. out.push_str(format!("render area: {:?}\n", lna.render_area()).as_str());
  294. for child in lna.child_iter() {
  295. dump_node_tree_helper(child, indent + 1, out);
  296. }
  297. }
  298. pub fn dump_node_tree(lna: LayoutNodeAccess, out: &mut String) {
  299. out.clear();
  300. dump_node_tree_helper(lna, 0, out);
  301. }
  302. /// Iterator implementation to iterate across children of a LayoutNodeContainer
  303. #[derive(Clone)]
  304. pub struct LayoutChildIter<'l> {
  305. lnc: &'l dyn LayoutNodeContainer,
  306. next_index: usize,
  307. }
  308. impl<'l> LayoutChildIter<'l> {
  309. fn new(lnc: &'l dyn LayoutNodeContainer) -> Self {
  310. Self { lnc, next_index: 0 }
  311. }
  312. }
  313. impl<'l> Iterator for LayoutChildIter<'l> {
  314. type Item = LayoutNodeAccess<'l>;
  315. fn next(&mut self) -> Option<Self::Item> {
  316. let index = self.next_index;
  317. if index >= self.lnc.layout_child_count() {
  318. None
  319. } else {
  320. self.next_index += 1;
  321. self.lnc.layout_child(index)
  322. }
  323. }
  324. }
  325. /// Wrapper struct to access a [`LayoutNodeContainer`].
  326. #[derive(Clone, Copy)]
  327. pub struct LayoutNodeAccess<'l> {
  328. lnc: &'l dyn LayoutNodeContainer,
  329. }
  330. impl<'l> LayoutNodeAccess<'l> {
  331. pub fn new(lnc: &'l dyn LayoutNodeContainer) -> Self {
  332. Self { lnc }
  333. }
  334. }
  335. impl<'l> Deref for LayoutNodeAccess<'l> {
  336. type Target = LayoutNode;
  337. fn deref(&self) -> &Self::Target {
  338. self.lnc.layout_node()
  339. }
  340. }
  341. impl<'l> LayoutNodeAccess<'l> {
  342. pub fn child(&self, ndx: usize) -> Option<LayoutNodeAccess<'l>> {
  343. self.lnc.layout_child(ndx)
  344. }
  345. pub fn child_len(&self) -> usize {
  346. self.lnc.layout_child_count()
  347. }
  348. pub fn child_iter(&self) -> LayoutChildIter {
  349. LayoutChildIter::new(self.lnc)
  350. }
  351. }
  352. /// Data source trait for LayoutNodeAccess.
  353. pub trait LayoutNodeContainer {
  354. fn layout_node(&self) -> &LayoutNode;
  355. fn layout_child(&self, ndx: usize) -> Option<LayoutNodeAccess<'_>>;
  356. fn layout_child_count(&self) -> usize;
  357. }
  358. /// Helper struct to store a leaf LayoutNode and automatically provide a LayoutNodeContainer impl
  359. pub struct LeafLayoutNode(LayoutNode);
  360. impl LeafLayoutNode {
  361. pub fn new(ln: LayoutNode) -> Self {
  362. Self(ln)
  363. }
  364. }
  365. impl From<LayoutNode> for LeafLayoutNode {
  366. fn from(value: LayoutNode) -> Self {
  367. Self::new(value)
  368. }
  369. }
  370. impl std::ops::Deref for LeafLayoutNode {
  371. type Target = LayoutNode;
  372. fn deref(&self) -> &Self::Target {
  373. &self.0
  374. }
  375. }
  376. impl std::ops::DerefMut for LeafLayoutNode {
  377. fn deref_mut(&mut self) -> &mut Self::Target {
  378. &mut self.0
  379. }
  380. }
  381. impl LayoutNodeContainer for LeafLayoutNode {
  382. fn layout_node(&self) -> &LayoutNode {
  383. &self.0
  384. }
  385. fn layout_child(&self, _ndx: usize) -> Option<LayoutNodeAccess<'_>> {
  386. None
  387. }
  388. fn layout_child_count(&self) -> usize {
  389. 0
  390. }
  391. }
  392. /// Helper LayoutNodeContainer implementation for LayoutNodes with one child
  393. pub struct LinearAccess<'l>(&'l LayoutNode, LayoutNodeAccess<'l>);
  394. impl<'l> LinearAccess<'l> {
  395. pub fn new(parent: &'l LayoutNode, child: LayoutNodeAccess<'l>) -> Self {
  396. Self(parent, child)
  397. }
  398. }
  399. impl<'l> LayoutNodeContainer for LinearAccess<'l> {
  400. fn layout_node(&self) -> &LayoutNode {
  401. self.0
  402. }
  403. fn layout_child(&self, ndx: usize) -> Option<LayoutNodeAccess> {
  404. if ndx == 0 {
  405. Some(self.1)
  406. } else {
  407. None
  408. }
  409. }
  410. fn layout_child_count(&self) -> usize {
  411. 1
  412. }
  413. }