1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//! Measurement system — equivalent to Rich's `measure.py`.
//!
//! Used during layout to determine the minimum and maximum widths of
//! renderable objects.
use crate::console::ConsoleOptions;
// ---------------------------------------------------------------------------
// Measurement
// ---------------------------------------------------------------------------
/// A range of valid widths for a renderable: [minimum, maximum].
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Measurement {
pub minimum: usize,
pub maximum: usize,
}
impl Measurement {
/// Create a new measurement.
pub fn new(minimum: usize, maximum: usize) -> Self {
Self { minimum, maximum }
}
/// Clamp the measurement to a given maximum.
pub fn with_maximum(self, maximum: usize) -> Self {
Self {
minimum: self.minimum.min(maximum),
maximum: self.maximum.min(maximum),
}
}
/// Clamp the measurement to a given minimum.
pub fn with_minimum(self, minimum: usize) -> Self {
Self {
minimum: self.minimum.max(minimum),
maximum: self.maximum.max(minimum),
}
}
/// Shrink both minimum and maximum by `amount`.
pub fn shrink(self, amount: usize) -> Self {
Self {
minimum: self.minimum.saturating_sub(amount),
maximum: self.maximum.saturating_sub(amount),
}
}
/// Grow both minimum and maximum by `amount`.
pub fn grow(self, amount: usize) -> Self {
Self {
minimum: self.minimum + amount,
maximum: self.maximum + amount,
}
}
/// Return a fixed-width measurement.
pub fn fixed(width: usize) -> Self {
Self {
minimum: width,
maximum: width,
}
}
}
// ---------------------------------------------------------------------------
// Measurement utilities (equivalent to `measure_renderables`)
// ---------------------------------------------------------------------------
/// Trait for objects that can report their width measurement.
pub trait Measurable {
fn measure(&self, options: &ConsoleOptions) -> Measurement;
}
impl Measurable for String {
fn measure(&self, _options: &ConsoleOptions) -> Measurement {
let w = unicode_width::UnicodeWidthStr::width(self.as_str());
Measurement::fixed(w)
}
}
impl Measurable for &str {
fn measure(&self, _options: &ConsoleOptions) -> Measurement {
let w = unicode_width::UnicodeWidthStr::width(*self);
Measurement::fixed(w)
}
}
/// Measure a collection of renderables and return the aggregate measurement.
///
/// The minimum is the max of all minimums; the maximum is the max of all
/// maximums (assuming items stack vertically, not horizontally).
pub fn measure_renderables<'a>(
items: impl IntoIterator<Item = &'a dyn Measurable>,
options: &ConsoleOptions,
) -> Measurement {
let mut min = 0usize;
let mut max = 0usize;
for item in items {
let m = item.measure(options);
min = min.max(m.minimum);
max = max.max(m.maximum);
}
Measurement::new(min, max)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_measurement_clamp() {
let m = Measurement::new(10, 100).with_maximum(50);
assert_eq!(m.minimum, 10);
assert_eq!(m.maximum, 50);
}
}