Skip to main content

oxiui_text/
label.rs

1//! Static text display widget state.
2//!
3//! [`Label`] tracks the displayed text string and layout constraints.  Pixel-
4//! accurate truncation requires a `TextPipeline` and is handled by adapters
5//! calling `crate::truncation::truncate`; the `is_truncated` flag lets callers
6//! communicate the result back to this state struct.
7
8// ── Label ─────────────────────────────────────────────────────────────────────
9
10/// State for a static text label widget.
11///
12/// This is a pure data structure.  Rendering and actual pixel-accurate
13/// truncation are handled by the caller (adapter layer) using the functions in
14/// `crate::truncation`.
15#[derive(Debug, Clone)]
16pub struct Label {
17    text: String,
18    /// Maximum number of lines to display before clipping or truncating.
19    max_lines: Option<usize>,
20    /// Set by the adapter when the text had to be truncated to fit.
21    truncated: bool,
22}
23
24impl Label {
25    /// Create a new `Label` with the given text and no line limit.
26    pub fn new(text: impl Into<String>) -> Self {
27        Self {
28            text: text.into(),
29            max_lines: None,
30            truncated: false,
31        }
32    }
33
34    /// Limit the label to `n` visible lines.
35    ///
36    /// When `n` is `1`, adapters should apply single-line ellipsis truncation
37    /// via `crate::truncation::truncate`.
38    pub fn with_max_lines(mut self, n: usize) -> Self {
39        self.max_lines = Some(n);
40        self
41    }
42
43    /// The raw text stored in this label.
44    pub fn text(&self) -> &str {
45        &self.text
46    }
47
48    /// The optional maximum line count.
49    pub fn max_lines(&self) -> Option<usize> {
50        self.max_lines
51    }
52
53    /// Returns `true` when an adapter reported that truncation occurred during
54    /// the last layout pass.
55    pub fn is_truncated(&self) -> bool {
56        self.truncated
57    }
58
59    /// Called by adapters after each layout pass to record whether truncation
60    /// occurred.
61    pub fn set_truncated(&mut self, truncated: bool) {
62        self.truncated = truncated;
63    }
64
65    /// Returns the text that should be displayed.
66    ///
67    /// For single-line labels (`max_lines == Some(1)`), pixel-accurate
68    /// truncation requires a `TextPipeline`; call `crate::truncation::truncate`
69    /// from the adapter and pass `set_truncated(true)` when the result
70    /// differs from the raw text.  This method returns the full raw text so the
71    /// adapter can measure and decide.
72    pub fn display_text(&self) -> &str {
73        &self.text
74    }
75}
76
77impl Default for Label {
78    fn default() -> Self {
79        Self::new("")
80    }
81}
82
83// ── Tests ─────────────────────────────────────────────────────────────────────
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn label_new() {
91        let label = Label::new("hello");
92        assert_eq!(label.text(), "hello");
93        assert!(label.max_lines().is_none());
94    }
95
96    #[test]
97    fn label_is_truncated_false_initially() {
98        let label = Label::new("hello world");
99        assert!(
100            !label.is_truncated(),
101            "newly created label must not be truncated"
102        );
103    }
104
105    #[test]
106    fn label_set_truncated() {
107        let mut label = Label::new("a very long text");
108        assert!(!label.is_truncated());
109        label.set_truncated(true);
110        assert!(label.is_truncated());
111    }
112
113    #[test]
114    fn label_with_max_lines() {
115        let label = Label::new("text").with_max_lines(1);
116        assert_eq!(label.max_lines(), Some(1));
117    }
118
119    #[test]
120    fn label_display_text_matches_raw() {
121        let label = Label::new("hello");
122        assert_eq!(label.display_text(), "hello");
123    }
124
125    #[test]
126    fn label_default() {
127        let label = Label::default();
128        assert_eq!(label.text(), "");
129        assert!(!label.is_truncated());
130    }
131}