retroglyph_widgets/align.rs
1//! [`Align`]: horizontal alignment of a single line of text within a
2//! fixed-width area.
3
4/// Horizontal alignment of one line of text within the columns it's rendered
5/// into.
6///
7/// A builder knob on the single-line text widgets ([`Text`](crate::Text),
8/// [`PrintLine`](crate::PrintLine)) and on the titles of [`Panel`](crate::Panel)
9/// and [`Modal`](crate::Modal). Text widgets default to [`Left`](Self::Left)
10/// (their long-standing behavior); panel/modal titles default to
11/// [`Center`](Self::Center) (theirs).
12#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
13pub enum Align {
14 /// Text starts at the left edge; leftover space trails on the right.
15 #[default]
16 Left,
17 /// Leftover space is split evenly on both sides (an odd extra column goes
18 /// on the right).
19 Center,
20 /// Text ends at the right edge; leftover space leads on the left.
21 Right,
22}
23
24impl Align {
25 /// The left offset, in columns, at which a `content_width`-column line
26 /// should start within an `area_width`-column area for this alignment.
27 ///
28 /// Saturates at `0` when the content is wider than the area, so the caller
29 /// clips from the left edge rather than underflowing.
30 #[must_use]
31 pub const fn offset(self, area_width: u16, content_width: u16) -> u16 {
32 let slack = area_width.saturating_sub(content_width);
33 match self {
34 Self::Left => 0,
35 Self::Center => slack / 2,
36 Self::Right => slack,
37 }
38 }
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44
45 #[test]
46 fn offset_places_content_per_alignment() {
47 // 4-column word in a 10-column area: 6 columns of slack.
48 assert_eq!(Align::Left.offset(10, 4), 0);
49 assert_eq!(Align::Center.offset(10, 4), 3);
50 assert_eq!(Align::Right.offset(10, 4), 6);
51 }
52
53 #[test]
54 fn center_puts_the_odd_column_on_the_right() {
55 // 4-column word in a 9-column area: 5 columns of slack, 2 on the left.
56 assert_eq!(Align::Center.offset(9, 4), 2);
57 }
58
59 #[test]
60 fn wider_than_area_saturates_to_zero() {
61 assert_eq!(Align::Left.offset(3, 8), 0);
62 assert_eq!(Align::Center.offset(3, 8), 0);
63 assert_eq!(Align::Right.offset(3, 8), 0);
64 }
65
66 #[test]
67 fn default_is_left() {
68 assert_eq!(Align::default(), Align::Left);
69 }
70}