Skip to main content

tui_lipan/widgets/divider/
mod.rs

1//! Divider widget.
2
3mod layout;
4mod node;
5mod reconcile;
6
7pub(crate) use self::reconcile::{DividerReconcile, reconcile_divider};
8pub use layout::measure_divider;
9pub use node::DividerNode;
10
11use crate::core::element::{Element, ElementKind};
12use crate::style::{Align, Length, Style};
13
14/// Layout orientation (horizontal vs vertical).
15#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
16pub enum Orientation {
17    /// Horizontal orientation.
18    Horizontal,
19    /// Vertical orientation.
20    Vertical,
21}
22
23/// A 1-cell-thick divider.
24#[derive(Clone)]
25pub struct Divider {
26    pub(crate) orientation: Orientation,
27    pub(crate) width: Length,
28    pub(crate) height: Length,
29    pub(crate) ch: char,
30    pub(crate) style: Style,
31    pub(crate) label: Option<Box<Element>>,
32    pub(crate) label_alignment: Align,
33    /// Left and right inset of a horizontal label, in cells.
34    ///
35    /// The divider character still paints in the inset cells, so a left inset of 1 yields
36    /// `─title` rather than a blank before the title. Only the label's own cells are left clear.
37    pub(crate) label_padding: (u16, u16),
38    pub(crate) join_frame: bool,
39}
40
41impl Divider {
42    /// Create a divider with the given orientation.
43    pub fn new(orientation: Orientation) -> Self {
44        match orientation {
45            Orientation::Horizontal => Self::horizontal(),
46            Orientation::Vertical => Self::vertical(),
47        }
48    }
49
50    /// Create a horizontal divider.
51    pub fn horizontal() -> Self {
52        Self {
53            orientation: Orientation::Horizontal,
54            width: Length::Flex(1),
55            height: Length::Px(1),
56            ch: '─',
57            style: Style::default(),
58            label: None,
59            label_alignment: Align::Start,
60            label_padding: (1, 1),
61            join_frame: false,
62        }
63    }
64
65    /// Create a vertical divider.
66    pub fn vertical() -> Self {
67        Self {
68            orientation: Orientation::Vertical,
69            width: Length::Px(1),
70            height: Length::Flex(1),
71            ch: '│',
72            style: Style::default(),
73            label: None,
74            label_alignment: Align::Start,
75            label_padding: (1, 1),
76            join_frame: false,
77        }
78    }
79
80    /// Set style.
81    pub fn style(mut self, style: Style) -> Self {
82        self.style = style;
83        self
84    }
85
86    /// Set an inline label (horizontal only).
87    pub fn label(mut self, label: impl Into<Element>) -> Self {
88        self.label = Some(Box::new(label.into()));
89        self
90    }
91
92    /// Set label alignment (horizontal only).
93    pub fn label_alignment(mut self, alignment: Align) -> Self {
94        self.label_alignment = alignment;
95        self
96    }
97
98    /// Set equal left and right label insets (horizontal only).
99    ///
100    /// Prefer [`Self::label_padding_axes`] when the sides should differ - for example a border
101    /// title that wants one leading `─` and no trailing gap before the line continues.
102    pub fn label_padding(mut self, padding: u16) -> Self {
103        self.label_padding = (padding, padding);
104        self
105    }
106
107    /// Set independent left/right label insets (horizontal only).
108    ///
109    /// Inset cells still show the divider character; only the label itself clears the line.
110    pub fn label_padding_axes(mut self, left: u16, right: u16) -> Self {
111        self.label_padding = (left, right);
112        self
113    }
114
115    /// Join the divider with the nearest frame border.
116    pub fn join_frame(mut self, join: bool) -> Self {
117        self.join_frame = join;
118        self
119    }
120
121    /// Override the divider character.
122    pub fn ch(mut self, ch: char) -> Self {
123        self.ch = ch;
124        self
125    }
126
127    /// Override requested width.
128    pub fn width(mut self, width: Length) -> Self {
129        self.width = width;
130        self
131    }
132
133    /// Override requested height.
134    pub fn height(mut self, height: Length) -> Self {
135        self.height = height;
136        self
137    }
138}
139
140impl From<Divider> for Element {
141    fn from(value: Divider) -> Self {
142        Element::new(ElementKind::Divider(value))
143    }
144}
145
146impl crate::layout::hash::LayoutHash for Divider {
147    fn layout_hash(
148        &self,
149        hasher: &mut impl std::hash::Hasher,
150        recurse: &dyn Fn(&Element) -> Option<u64>,
151    ) -> Option<()> {
152        use std::hash::Hash;
153        self.width.hash(hasher);
154        self.height.hash(hasher);
155        self.orientation.hash(hasher);
156        self.label_alignment.hash(hasher);
157        self.label_padding.hash(hasher);
158        self.join_frame.hash(hasher);
159
160        if let Some(label) = self.label.as_deref() {
161            recurse(label)?.hash(hasher);
162        }
163        Some(())
164    }
165}