Skip to main content

rich/
constrain.rs

1//! Width constraint.
2//!
3//! Port of upstream `rich/constrain.py`. [`Constrain`] renders a child within a
4//! reduced maximum width.
5
6use crate::console::{Console, ConsoleOptions};
7use crate::measure::Measurement;
8use crate::protocol::Renderable;
9use crate::segment::Segment;
10
11/// Limits a child renderable to at most `width` cells. Mirrors `rich.constrain.Constrain`.
12pub struct Constrain {
13    child: Box<dyn Renderable>,
14    width: Option<usize>,
15}
16
17impl Constrain {
18    /// Constrain `child` to `width` cells (or leave unconstrained when `None`).
19    pub fn new(child: Box<dyn Renderable>, width: Option<usize>) -> Self {
20        Constrain { child, width }
21    }
22}
23
24impl Renderable for Constrain {
25    fn rich_render(&self, console: &Console, options: &ConsoleOptions) -> Vec<Segment> {
26        let width = match self.width {
27            Some(width) => width.min(options.max_width),
28            None => options.max_width,
29        };
30        let child_options = options.update_width(width);
31        // `console.render(self.renderable, child_options)`, which yields
32        // nothing in no width.
33        console.render(self.child.as_ref(), Some(&child_options))
34    }
35
36    /// Port of `Constrain.__rich_measure__`: the child measured within the
37    /// constraint (`Measurement.get` then caps it at the outer width).
38    fn measure(&self, console: &Console, options: &ConsoleOptions) -> Measurement {
39        let options = match self.width {
40            Some(width) => options.update_width(width),
41            None => options.clone(),
42        };
43        Measurement::get(console, &options, self.child.as_ref())
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50    use crate::color::ColorSystem;
51    use crate::panel::Panel;
52    use crate::r#box::SQUARE;
53    use crate::text::Text;
54
55    #[test]
56    fn constrains_panel_width() {
57        let console = Console::builder()
58            .force_terminal(true)
59            .color_system(Some(ColorSystem::Truecolor))
60            .width(20)
61            .build();
62        let panel = Panel::new(Box::new(Text::new("hi"))).box_set(SQUARE);
63        let constrained = Constrain::new(Box::new(panel), Some(10));
64        let out = console.render_export(&constrained);
65        assert_eq!(out, "┌────────┐\n│ hi     │\n└────────┘\n");
66    }
67}