layout.rs 14 KB

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