urushi/view/geometry.rs
1//! The two numbers resolution maps between: the area a view is given, and the
2//! size it resolved to.
3
4/// The size of a resolved rectangle, in terminal cells.
5#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
6pub struct Size {
7 width: usize,
8 height: usize,
9}
10
11impl Size {
12 /// The empty rectangle.
13 pub const ZERO: Self = Self {
14 width: 0,
15 height: 0,
16 };
17
18 pub const fn new(width: usize, height: usize) -> Self {
19 Self { width, height }
20 }
21
22 pub const fn width(&self) -> usize {
23 self.width
24 }
25
26 pub const fn height(&self) -> usize {
27 self.height
28 }
29
30 /// Returns whether the rectangle occupies no cells.
31 pub const fn is_empty(&self) -> bool {
32 self.width == 0 || self.height == 0
33 }
34}
35
36/// The area a view may occupy: an input to layout, not an afterthought.
37///
38/// A terminal width or a Ratatui `Rect` becomes an `Available`. It flows down
39/// the tree and each node resolves its own size under it, so a frame closes at
40/// whatever size the area forces. It is not a clip applied to a finished
41/// rectangle; the only crop left is the degenerate-case safety net in
42/// [`resolve`](super::resolve).
43///
44/// An absent bound is not zero: it means the axis is unbounded, which is what
45/// `measure` resolves under and what makes an intrinsic size the same
46/// computation as a bounded one.
47#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
48pub struct Available {
49 width: Option<usize>,
50 height: Option<usize>,
51}
52
53impl Available {
54 /// Imposes no bound on either axis.
55 pub const NONE: Self = Self {
56 width: None,
57 height: None,
58 };
59
60 pub const fn new(width: Option<usize>, height: Option<usize>) -> Self {
61 Self { width, height }
62 }
63
64 /// Bounds the width only — a terminal of a known width and no known
65 /// height.
66 pub const fn columns(width: usize) -> Self {
67 Self::new(Some(width), None)
68 }
69
70 /// Bounds both axes.
71 pub const fn size(width: usize, height: usize) -> Self {
72 Self::new(Some(width), Some(height))
73 }
74
75 pub const fn width(&self) -> Option<usize> {
76 self.width
77 }
78
79 pub const fn height(&self) -> Option<usize> {
80 self.height
81 }
82}
83
84/// The two distinct facts carried internally for one layout axis.
85///
86/// `reference` is the finite size that area-dependent claims such as `Fill`
87/// divide. `cap` is the bound that may shrink content. Ordinary public
88/// [`Available`] values initialize both to the same extent; a projected axis
89/// keeps the reference while removing the child cap.
90#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
91pub(super) struct Constraint {
92 pub reference: Option<usize>,
93 pub cap: Option<usize>,
94}
95
96impl Constraint {
97 pub const fn unbounded() -> Self {
98 Self {
99 reference: None,
100 cap: None,
101 }
102 }
103
104 pub const fn available(extent: Option<usize>) -> Self {
105 Self {
106 reference: extent,
107 cap: extent,
108 }
109 }
110
111 pub const fn established(extent: usize) -> Self {
112 Self::available(Some(extent))
113 }
114}