rustty_oxide/
label.rs

1use rustty::{CellAccessor, Size, HasSize};
2
3use core::{
4    Alignable,
5    HorizontalAlign,
6    VerticalAlign,
7    Widget,
8    Frame,
9    Painter,
10};
11
12/// Display text to widgets
13///
14/// # Examples
15///
16/// ```
17/// use oxide::core::{VerticalAlign, HorizontalAlign, Widget};
18/// use oxide::{Dialog, Label};
19///
20/// let mut maindlg = Dialog::new(60, 10);
21///
22/// let mut label = Label::from_str("Hi, this is an example!");
23/// label.pack(&maindlg, HorizontalAlign::Middle, VerticalAlign::Middle, (0,0));
24///
25/// maindlg.add_label(label);
26/// maindlg.draw_box();
27/// ```
28///
29pub struct Label {
30    frame: Frame,
31    text: Vec<String>,
32    x: usize,
33    y: usize,
34    t_halign: HorizontalAlign,
35    t_valign: VerticalAlign,
36    t_margin: (usize, usize)
37}
38
39impl Label {
40    /// Construct a new Label widget `cols` wide by `rols` high. Initial text is empty
41    /// and left aligned
42    ///
43    /// # Examples
44    ///
45    /// ```
46    /// use oxide::Label;
47    ///
48    /// let mut label = Label::new(60, 10);
49    /// ```
50    ///
51    pub fn new(cols: usize, rows: usize) -> Label {
52        Label {
53            frame: Frame::new(cols, rows),
54            text: Vec::new(),
55            x: 0,
56            y: 0,
57            t_halign: HorizontalAlign::Left,
58            t_valign: VerticalAlign::Middle,
59            t_margin: (0, 0),
60        }
61    }
62
63    /// Construct a new label widget from an existing string *s*. *s* can either be a
64    /// `&str` or `String` , and a label will be constructed that is the size of the 
65    /// length of characters in *s*. Text is left aligned by default
66    ///
67    /// # Examples
68    ///
69    /// ```
70    /// use oxide::Label;
71    ///
72    /// let mut label1 = Label::from_str("This is a label");    // label is size (15x1)
73    ///
74    /// let s = "Here's another label".to_string();
75    /// let mut label2 = Label::from_str(s);                    // label is size (20x1)
76    /// ```
77    ///
78    pub fn from_str<S: Into<String>>(s: S) -> Label {
79        let s = s.into();
80        Label {
81            frame: Frame::new(s.len(), 1),
82            text: vec![s.into()],
83            x: 0,
84            y: 0,
85            t_halign: HorizontalAlign::Left,
86            t_valign: VerticalAlign::Middle,
87            t_margin: (0, 0),
88        }
89    }
90
91    /// Construct a new label widget from an existing string *s*. *s* can either be a
92    /// `&str` or `String` , and a label will be constructed that is the size of the 
93    /// length of characters in *s*. Text is left aligned by default
94    ///
95    /// # Examples
96    ///
97    /// ```
98    /// use oxide::Label;
99    ///
100    /// let mut label1 = Label::from_str("This is a label");    // label is size (15x1)
101    ///
102    /// let s = "Here's another label".to_string();
103    /// let mut label2 = Label::from_str(s);                    // label is size (20x1)
104    /// ```
105    ///
106    pub fn from_str_ref(s: &str) -> Label 
107    {
108        Label {
109            frame: Frame::new(s.len(), 1),
110            text: vec![s.to_string()],
111            x: 0,
112            y: 0,
113            t_halign: HorizontalAlign::Left,
114            t_valign: VerticalAlign::Middle,
115            t_margin: (0, 0),
116        }
117    }
118    /// Specify a custom alignment for the text within the widget. Each line
119    /// drawn within the label will adhere to the alignments passed for the
120    /// text. *note that text alignment is with respect to the *label*
121    ///
122    /// # Examples
123    ///
124    /// ```
125    /// use oxide::core::{HorizontalAlign, VerticalAlign};
126    /// use oxide::Label;
127    ///
128    /// let mut label = Label::new(20, 3);
129    /// label.set_text("Centered");
130    /// label.align_text(HorizontalAlign::Middle, VerticalAlign::Middle, (0,0));
131    /// ```
132    ///
133    pub fn align_text(&mut self, halign: HorizontalAlign, valign: VerticalAlign,
134                    margin: (usize, usize)) {
135        self.t_halign = halign;
136        self.t_valign = valign;
137        self.t_margin = margin;
138    }
139
140    /// Set the text of the widget to the passed `&str` or `String`. If the
141    /// widget does not have enough room to display the new text, the label 
142    /// will only show the truncated text. *resize()* must be called to extend
143    /// the size of the label.
144    ///
145    /// # Examples
146    ///
147    /// ```
148    /// use rustty::HasSize;
149    /// use oxide::core::Widget;
150    /// use oxide::Label;
151    ///
152    /// let mut label1 = Label::new(20, 3);
153    /// label1.set_text("Initial text");
154    /// ```
155    ///
156    pub fn set_text<S: Into<String>>(&mut self, new_str: S) { 
157        let (framex, _) = self.frame.size();
158        self.text = Vec::new();
159        let mut parse = new_str.into();
160        let mut line = String::new();
161
162        // This loop below will accomplish splitting a line of text
163        // into lines that adhere to the amount of rows in a label
164        loop {
165            // Look for a word until a whitespace is reached
166            if let Some(loc) = parse.find(char::is_whitespace) {
167                let line_len = line.len();
168                let tmp = parse[..loc].to_owned();
169                // If the word can fit on the current line, add it
170                if line_len + tmp.len() + self.t_margin.0 < framex {
171                    line.push_str(&tmp);
172                } else {
173                    line = line.trim_right().to_owned();
174                    self.text.push(line);
175                    line = tmp.to_owned();
176                }
177                parse = parse[loc..].to_owned();
178            } else {
179                // If no whitespace detected, there may still be one
180                // more word so attempt to add it
181                if parse.len() != 0 {
182                    let line_len = line.len();
183                    if line_len + parse.len() + self.t_margin.0 < framex {
184                        line.push_str(&parse);
185                        self.text.push(line);
186                    } else {
187                        self.text.push(line);
188                        self.text.push(parse);
189                    }
190                }
191                break;
192            }
193
194            // Look for the range of spaces between words
195            if let Some(loc) = parse.find(|c: char| c != ' ') {
196                let line_len = line.len();
197                let tmp = parse[..loc].to_owned();
198                // If the next word can fit on the current line, do so
199                if line_len + tmp.len() + self.t_margin.0 < framex {
200                    line.push_str(&tmp);
201                } else {
202                    line = line.trim_right().to_owned();
203                    self.text.push(line);
204                    line = "".to_string();
205                }
206                parse = parse[loc..].to_owned();
207            } else {
208                // We don't care if there's spaces at the end, so don't check
209                break;
210            }
211        }
212    }
213}
214
215impl Widget for Label {
216    fn draw(&mut self, parent: &mut CellAccessor) {
217        // For every line to be written, align it correctly as defined by the user in 
218        // align_text, if not this text will be left and middle aligned by default
219        for (i, item) in self.text.iter().enumerate() {
220            self.x = self.frame.halign_line(&item, self.t_halign.clone(), self.t_margin.0);
221            self.y = self.frame.valign_line(&item, self.t_valign.clone(), self.t_margin.1);
222            self.frame.printline(self.x, self.y + i, &item);
223        }
224        self.frame.draw_into(parent);
225    }
226
227    fn pack(&mut self, parent: &HasSize, halign: HorizontalAlign, valign: VerticalAlign,
228                margin: (usize, usize)) {
229        self.frame.align(parent, halign, valign, margin);
230    }
231
232    fn draw_box(&mut self) {
233        self.frame.draw_box();
234    }
235
236    fn resize(&mut self, new_size: Size) {
237        self.frame.resize(new_size);
238    }
239
240    fn frame(&self) -> &Frame {
241        &self.frame
242    }
243
244    fn frame_mut(&mut self) -> &mut Frame {
245        &mut self.frame
246    }
247}