Skip to main content

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}