1use crate::view::Size;
5use crate::{
6 Align, Border, Color, Hyperlink, Length, Overflow, Sides, TextAttribute, TextAttributes,
7 TextStyle, Underline, UnderlineStyle, VerticalAlign,
8};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct BlockStyle {
27 text_style: TextStyle,
28 padding: Sides,
29 margin: Sides,
30 border: Option<Border>,
31 border_top: bool,
32 border_right: bool,
33 border_bottom: bool,
34 border_left: bool,
35 border_text_style: TextStyle,
36 border_foreground: Option<Color>,
37 border_background: Option<Color>,
38 width: Option<Length>,
39 height: Option<Length>,
40 min_width: Option<u16>,
41 min_height: Option<u16>,
42 max_width: Option<u16>,
43 max_height: Option<u16>,
44 overflow: Overflow,
45 align: Align,
46 vertical_align: VerticalAlign,
47}
48
49impl Default for BlockStyle {
50 fn default() -> Self {
51 Self {
52 text_style: TextStyle::new(),
53 padding: Sides::default(),
54 margin: Sides::default(),
55 border: None,
56 border_top: true,
57 border_right: true,
58 border_bottom: true,
59 border_left: true,
60 border_text_style: TextStyle::new(),
61 border_foreground: None,
62 border_background: None,
63 width: None,
64 height: None,
65 min_width: None,
66 min_height: None,
67 max_width: None,
68 max_height: None,
69 overflow: Overflow::default(),
70 align: Align::default(),
71 vertical_align: VerticalAlign::default(),
72 }
73 }
74}
75
76impl BlockStyle {
77 pub fn new() -> Self {
78 Self::default()
79 }
80
81 pub fn from_text_style(text_style: TextStyle) -> Self {
83 Self {
84 text_style,
85 ..Self::default()
86 }
87 }
88
89 pub const fn text_style(&self) -> &TextStyle {
91 &self.text_style
92 }
93
94 pub fn foreground(mut self, color: impl Into<Color>) -> Self {
96 self.text_style = self.text_style.foreground(color);
97 self
98 }
99
100 pub fn reset_foreground(mut self) -> Self {
102 self.text_style = self.text_style.reset_foreground();
103 self
104 }
105
106 pub fn background(mut self, color: impl Into<Color>) -> Self {
108 self.text_style = self.text_style.background(color);
109 self
110 }
111
112 pub fn reset_background(mut self) -> Self {
114 self.text_style = self.text_style.reset_background();
115 self
116 }
117
118 pub fn add_attribute(mut self, attribute: TextAttribute) -> Self {
120 self.text_style = self.text_style.add_attribute(attribute);
121 self
122 }
123
124 pub fn add_attributes(mut self, attributes: TextAttributes) -> Self {
126 self.text_style = self.text_style.add_attributes(attributes);
127 self
128 }
129
130 pub fn remove_attribute(mut self, attribute: TextAttribute) -> Self {
132 self.text_style = self.text_style.remove_attribute(attribute);
133 self
134 }
135
136 pub fn remove_attributes(mut self, attributes: TextAttributes) -> Self {
138 self.text_style = self.text_style.remove_attributes(attributes);
139 self
140 }
141
142 pub fn reset_attributes(mut self) -> Self {
144 self.text_style = self.text_style.reset_attributes();
145 self
146 }
147
148 pub fn bold(self) -> Self {
149 self.add_attribute(TextAttribute::Bold)
150 }
151
152 pub fn dim(self) -> Self {
153 self.add_attribute(TextAttribute::Dim)
154 }
155
156 pub fn italic(self) -> Self {
157 self.add_attribute(TextAttribute::Italic)
158 }
159
160 pub fn underlined(mut self) -> Self {
162 self.text_style = self.text_style.underlined();
163 self
164 }
165
166 pub fn underline_style(mut self, style: UnderlineStyle) -> Self {
169 self.text_style = self.text_style.underline_style(style);
170 self
171 }
172
173 pub fn underline_color(mut self, color: impl Into<Color>) -> Self {
176 self.text_style = self.text_style.underline_color(color);
177 self
178 }
179
180 pub fn underline(mut self, underline: Underline) -> Self {
182 self.text_style = self.text_style.underline(underline);
183 self
184 }
185
186 pub fn reset_underline(mut self) -> Self {
188 self.text_style = self.text_style.reset_underline();
189 self
190 }
191
192 pub fn hyperlink(mut self, hyperlink: impl Into<Hyperlink>) -> Self {
194 self.text_style = self.text_style.hyperlink(hyperlink);
195 self
196 }
197
198 pub fn reset_hyperlink(mut self) -> Self {
200 self.text_style = self.text_style.reset_hyperlink();
201 self
202 }
203
204 pub fn blink(self) -> Self {
205 self.add_attribute(TextAttribute::SlowBlink)
206 }
207
208 pub fn reverse(self) -> Self {
209 self.add_attribute(TextAttribute::Reversed)
210 }
211
212 pub fn hide(self) -> Self {
213 self.add_attribute(TextAttribute::Hidden)
214 }
215
216 pub fn strikethrough(self) -> Self {
217 self.add_attribute(TextAttribute::CrossedOut)
218 }
219
220 pub fn padding(mut self, sides: impl Into<Sides>) -> Self {
222 self.padding = sides.into();
223 self
224 }
225
226 pub fn reset_padding(mut self) -> Self {
228 self.padding = Sides::default();
229 self
230 }
231
232 pub fn margin(mut self, sides: impl Into<Sides>) -> Self {
234 self.margin = sides.into();
235 self
236 }
237
238 pub fn reset_margin(mut self) -> Self {
240 self.margin = Sides::default();
241 self
242 }
243
244 pub fn border(mut self, border: Border) -> Self {
249 self.border = Some(border);
250 self
251 }
252
253 pub fn reset_border(mut self) -> Self {
255 self.border = None;
256 self
257 }
258
259 pub fn border_top(mut self, enabled: bool) -> Self {
261 self.border_top = enabled;
262 self
263 }
264
265 pub fn reset_border_top(mut self) -> Self {
267 self.border_top = true;
268 self
269 }
270
271 pub fn border_right(mut self, enabled: bool) -> Self {
273 self.border_right = enabled;
274 self
275 }
276
277 pub fn reset_border_right(mut self) -> Self {
279 self.border_right = true;
280 self
281 }
282
283 pub fn border_bottom(mut self, enabled: bool) -> Self {
285 self.border_bottom = enabled;
286 self
287 }
288
289 pub fn reset_border_bottom(mut self) -> Self {
291 self.border_bottom = true;
292 self
293 }
294
295 pub fn border_left(mut self, enabled: bool) -> Self {
297 self.border_left = enabled;
298 self
299 }
300
301 pub fn reset_border_left(mut self) -> Self {
303 self.border_left = true;
304 self
305 }
306
307 pub fn border_text_style(mut self, style: TextStyle) -> Self {
312 self.border_text_style = style;
313 self
314 }
315
316 pub fn reset_border_text_style(mut self) -> Self {
318 self.border_text_style = TextStyle::default();
319 self
320 }
321
322 pub fn border_foreground(mut self, color: impl Into<Color>) -> Self {
324 self.border_foreground = Some(color.into());
325 self
326 }
327
328 pub fn reset_border_foreground(mut self) -> Self {
330 self.border_foreground = None;
331 self
332 }
333
334 pub fn border_background(mut self, color: impl Into<Color>) -> Self {
336 self.border_background = Some(color.into());
337 self
338 }
339
340 pub fn reset_border_background(mut self) -> Self {
342 self.border_background = None;
343 self
344 }
345
346 pub fn width(mut self, width: impl Into<Length>) -> Self {
353 self.width = Some(width.into());
354 self
355 }
356
357 pub fn reset_width(mut self) -> Self {
359 self.width = None;
360 self
361 }
362
363 pub fn height(mut self, height: impl Into<Length>) -> Self {
369 self.height = Some(height.into());
370 self
371 }
372
373 pub fn reset_height(mut self) -> Self {
375 self.height = None;
376 self
377 }
378
379 pub fn min_width(mut self, width: u16) -> Self {
381 self.min_width = Some(width);
382 self
383 }
384
385 pub fn reset_min_width(mut self) -> Self {
387 self.min_width = None;
388 self
389 }
390
391 pub fn min_height(mut self, height: u16) -> Self {
393 self.min_height = Some(height);
394 self
395 }
396
397 pub fn reset_min_height(mut self) -> Self {
399 self.min_height = None;
400 self
401 }
402
403 pub fn max_width(mut self, width: u16) -> Self {
406 self.max_width = Some(width);
407 self
408 }
409
410 pub fn reset_max_width(mut self) -> Self {
412 self.max_width = None;
413 self
414 }
415
416 pub fn max_height(mut self, height: u16) -> Self {
419 self.max_height = Some(height);
420 self
421 }
422
423 pub fn reset_max_height(mut self) -> Self {
425 self.max_height = None;
426 self
427 }
428
429 pub fn overflow(mut self, overflow: Overflow) -> Self {
431 self.overflow = overflow;
432 self
433 }
434
435 pub fn reset_overflow(mut self) -> Self {
437 self.overflow = Overflow::default();
438 self
439 }
440
441 pub fn align(mut self, align: Align) -> Self {
443 self.align = align;
444 self
445 }
446
447 pub fn reset_align(mut self) -> Self {
449 self.align = Align::default();
450 self
451 }
452
453 pub fn vertical_align(mut self, align: VerticalAlign) -> Self {
455 self.vertical_align = align;
456 self
457 }
458
459 pub fn reset_vertical_align(mut self) -> Self {
461 self.vertical_align = VerticalAlign::default();
462 self
463 }
464
465 pub const fn get_foreground(&self) -> Option<Color> {
467 self.text_style.get_foreground()
468 }
469
470 pub const fn get_background(&self) -> Option<Color> {
472 self.text_style.get_background()
473 }
474
475 pub const fn get_underline(&self) -> Option<Underline> {
477 self.text_style.get_underline()
478 }
479
480 pub const fn get_attributes(&self) -> TextAttributes {
482 self.text_style.get_attributes()
483 }
484
485 pub const fn get_padding(&self) -> Sides {
487 self.padding
488 }
489
490 pub const fn get_margin(&self) -> Sides {
492 self.margin
493 }
494
495 pub const fn get_border(&self) -> Option<Border> {
497 self.border
498 }
499
500 pub const fn get_border_top(&self) -> bool {
502 self.border_top
503 }
504
505 pub const fn get_border_right(&self) -> bool {
507 self.border_right
508 }
509
510 pub const fn get_border_bottom(&self) -> bool {
512 self.border_bottom
513 }
514
515 pub const fn get_border_left(&self) -> bool {
517 self.border_left
518 }
519
520 pub const fn get_border_text_style(&self) -> &TextStyle {
522 &self.border_text_style
523 }
524
525 pub const fn get_border_foreground(&self) -> Option<Color> {
527 self.border_foreground
528 }
529
530 pub const fn get_border_background(&self) -> Option<Color> {
532 self.border_background
533 }
534
535 pub const fn get_width(&self) -> Option<Length> {
537 self.width
538 }
539
540 pub const fn get_height(&self) -> Option<Length> {
542 self.height
543 }
544
545 pub const fn get_min_width(&self) -> Option<u16> {
547 self.min_width
548 }
549
550 pub const fn get_min_height(&self) -> Option<u16> {
552 self.min_height
553 }
554
555 pub const fn get_max_width(&self) -> Option<u16> {
557 self.max_width
558 }
559
560 pub const fn get_max_height(&self) -> Option<u16> {
562 self.max_height
563 }
564
565 pub const fn get_overflow(&self) -> &Overflow {
567 &self.overflow
568 }
569
570 pub fn frame_size(&self) -> Size {
576 let padding = self.padding;
577 let (left, right, top, bottom) = match self.border {
578 Some(_) => (
579 usize::from(self.border_left),
580 usize::from(self.border_right),
581 usize::from(self.border_top),
582 usize::from(self.border_bottom),
583 ),
584 None => (0, 0, 0, 0),
585 };
586 Size::new(
587 left + right + usize::from(padding.left) + usize::from(padding.right),
588 top + bottom + usize::from(padding.top) + usize::from(padding.bottom),
589 )
590 }
591
592 pub const fn get_align(&self) -> Align {
594 self.align
595 }
596
597 pub const fn get_vertical_align(&self) -> VerticalAlign {
599 self.vertical_align
600 }
601
602 pub(crate) fn border_style(&self) -> TextStyle {
604 let mut style = self.border_text_style.clone();
605 if let Some(color) = self.border_foreground {
606 style = style.foreground(color);
607 }
608 if let Some(color) = self.border_background {
609 style = style.background(color);
610 }
611 style
612 }
613}
614
615#[cfg(test)]
616mod tests {
617 use super::*;
618
619 #[test]
620 fn reset_builders_restore_every_property_default() {
621 let style = BlockStyle::new()
622 .foreground(Color::RED)
623 .background(Color::BLUE)
624 .add_attribute(TextAttribute::Italic)
625 .underline_color(Color::GREEN)
626 .hyperlink("https://example.com")
627 .padding(1)
628 .margin(2)
629 .border(Border::ROUNDED)
630 .border_top(false)
631 .border_right(false)
632 .border_bottom(false)
633 .border_left(false)
634 .border_text_style(TextStyle::new().bold())
635 .border_foreground(Color::CYAN)
636 .border_background(Color::BLACK)
637 .width(20)
638 .height(4)
639 .min_width(8)
640 .min_height(2)
641 .max_width(30)
642 .max_height(6)
643 .overflow(Overflow::ellipsis())
644 .align(Align::Center)
645 .vertical_align(VerticalAlign::Bottom)
646 .reset_foreground()
647 .reset_background()
648 .reset_attributes()
649 .reset_underline()
650 .reset_hyperlink()
651 .reset_padding()
652 .reset_margin()
653 .reset_border()
654 .reset_border_top()
655 .reset_border_right()
656 .reset_border_bottom()
657 .reset_border_left()
658 .reset_border_text_style()
659 .reset_border_foreground()
660 .reset_border_background()
661 .reset_width()
662 .reset_height()
663 .reset_min_width()
664 .reset_min_height()
665 .reset_max_width()
666 .reset_max_height()
667 .reset_overflow()
668 .reset_align()
669 .reset_vertical_align();
670
671 assert_eq!(style, BlockStyle::new());
672 }
673
674 #[test]
675 fn attribute_operations_accept_sets() {
676 let attributes = TextAttribute::Bold | TextAttribute::Italic;
677 let style = BlockStyle::new()
678 .add_attributes(attributes)
679 .remove_attribute(TextAttribute::Italic);
680
681 assert_eq!(style.get_attributes(), TextAttribute::Bold.into());
682 }
683}