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}