1#![forbid(unsafe_code)]
2
3use crate::buffer::Buffer;
13use crate::cell::{Cell, CellContent};
14use crate::grapheme_width;
15use ftui_core::geometry::Rect;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub struct BorderChars {
23 pub top_left: char,
25 pub top_right: char,
27 pub bottom_left: char,
29 pub bottom_right: char,
31 pub horizontal: char,
33 pub vertical: char,
35}
36
37impl BorderChars {
38 pub const SQUARE: Self = Self {
40 top_left: '┌',
41 top_right: '┐',
42 bottom_left: '└',
43 bottom_right: '┘',
44 horizontal: '─',
45 vertical: '│',
46 };
47
48 pub const ROUNDED: Self = Self {
50 top_left: '╭',
51 top_right: '╮',
52 bottom_left: '╰',
53 bottom_right: '╯',
54 horizontal: '─',
55 vertical: '│',
56 };
57
58 pub const DOUBLE: Self = Self {
60 top_left: '╔',
61 top_right: '╗',
62 bottom_left: '╚',
63 bottom_right: '╝',
64 horizontal: '═',
65 vertical: '║',
66 };
67
68 pub const HEAVY: Self = Self {
70 top_left: '┏',
71 top_right: '┓',
72 bottom_left: '┗',
73 bottom_right: '┛',
74 horizontal: '━',
75 vertical: '┃',
76 };
77
78 pub const ASCII: Self = Self {
80 top_left: '+',
81 top_right: '+',
82 bottom_left: '+',
83 bottom_right: '+',
84 horizontal: '-',
85 vertical: '|',
86 };
87}
88
89pub trait Draw {
91 fn draw_horizontal_line(&mut self, x: u16, y: u16, width: u16, cell: Cell);
93
94 fn draw_vertical_line(&mut self, x: u16, y: u16, height: u16, cell: Cell);
96
97 fn draw_rect_filled(&mut self, rect: Rect, cell: Cell);
99
100 fn draw_rect_outline(&mut self, rect: Rect, cell: Cell);
102
103 fn print_text(&mut self, x: u16, y: u16, text: &str, base_cell: Cell) -> u16;
108
109 fn print_text_clipped(
114 &mut self,
115 x: u16,
116 y: u16,
117 text: &str,
118 base_cell: Cell,
119 max_x: u16,
120 ) -> u16;
121
122 fn draw_border(&mut self, rect: Rect, chars: BorderChars, base_cell: Cell);
127
128 fn draw_box(&mut self, rect: Rect, chars: BorderChars, border_cell: Cell, fill_cell: Cell);
134
135 fn paint_area(
140 &mut self,
141 rect: Rect,
142 fg: Option<crate::cell::PackedRgba>,
143 bg: Option<crate::cell::PackedRgba>,
144 );
145}
146
147impl Draw for Buffer {
148 fn draw_horizontal_line(&mut self, x: u16, y: u16, width: u16, cell: Cell) {
149 for i in 0..width {
150 self.set(x.saturating_add(i), y, cell);
151 }
152 }
153
154 fn draw_vertical_line(&mut self, x: u16, y: u16, height: u16, cell: Cell) {
155 for i in 0..height {
156 self.set(x, y.saturating_add(i), cell);
157 }
158 }
159
160 fn draw_rect_filled(&mut self, rect: Rect, cell: Cell) {
161 self.fill(rect, cell);
162 }
163
164 fn draw_rect_outline(&mut self, rect: Rect, cell: Cell) {
165 if rect.is_empty() {
166 return;
167 }
168
169 self.draw_horizontal_line(rect.x, rect.y, rect.width, cell);
171
172 if rect.height > 1 {
174 self.draw_horizontal_line(rect.x, rect.bottom().saturating_sub(1), rect.width, cell);
175 }
176
177 if rect.height > 2 {
179 self.draw_vertical_line(rect.x, rect.y.saturating_add(1), rect.height - 2, cell);
180 }
181
182 if rect.width > 1 && rect.height > 2 {
184 self.draw_vertical_line(
185 rect.right().saturating_sub(1),
186 rect.y.saturating_add(1),
187 rect.height - 2,
188 cell,
189 );
190 }
191 }
192
193 fn print_text(&mut self, x: u16, y: u16, text: &str, base_cell: Cell) -> u16 {
194 self.print_text_clipped(x, y, text, base_cell, self.width())
195 }
196
197 fn print_text_clipped(
198 &mut self,
199 x: u16,
200 y: u16,
201 text: &str,
202 base_cell: Cell,
203 max_x: u16,
204 ) -> u16 {
205 use unicode_segmentation::UnicodeSegmentation;
206
207 let mut cx = x;
208 for grapheme in text.graphemes(true) {
209 if cx >= max_x {
210 break;
211 }
212
213 let Some(first) = grapheme.chars().next() else {
214 continue;
215 };
216
217 let mut width = grapheme_width(grapheme);
220 if width == 0 {
221 width = CellContent::from_char(first).width();
222 }
223 if width == 0 {
224 continue;
225 }
226
227 if cx as u32 + width as u32 > max_x as u32 {
229 break;
230 }
231
232 let cell = Cell {
233 content: CellContent::from_char(first),
234 fg: base_cell.fg,
235 bg: base_cell.bg,
236 attrs: base_cell.attrs,
237 };
238 self.set(cx, y, cell);
239
240 cx = cx.saturating_add(width as u16);
241 }
242 cx
243 }
244
245 fn draw_border(&mut self, rect: Rect, chars: BorderChars, base_cell: Cell) {
246 if rect.is_empty() {
247 return;
248 }
249
250 let make_cell = |c: char| -> Cell {
251 Cell {
252 content: CellContent::from_char(c),
253 fg: base_cell.fg,
254 bg: base_cell.bg,
255 attrs: base_cell.attrs,
256 }
257 };
258
259 let h_cell = make_cell(chars.horizontal);
260 let v_cell = make_cell(chars.vertical);
261
262 for x in rect.left()..rect.right() {
264 self.set(x, rect.top(), h_cell);
265 }
266
267 if rect.height > 1 {
269 for x in rect.left()..rect.right() {
270 self.set(x, rect.bottom().saturating_sub(1), h_cell);
271 }
272 }
273
274 if rect.height > 2 {
276 for y in (rect.top().saturating_add(1))..(rect.bottom().saturating_sub(1)) {
277 self.set(rect.left(), y, v_cell);
278 }
279 }
280
281 if rect.width > 1 && rect.height > 2 {
283 for y in (rect.top().saturating_add(1))..(rect.bottom().saturating_sub(1)) {
284 self.set(rect.right().saturating_sub(1), y, v_cell);
285 }
286 }
287
288 self.set(rect.left(), rect.top(), make_cell(chars.top_left));
290
291 if rect.width > 1 {
292 self.set(
293 rect.right().saturating_sub(1),
294 rect.top(),
295 make_cell(chars.top_right),
296 );
297 }
298
299 if rect.height > 1 {
300 self.set(
301 rect.left(),
302 rect.bottom().saturating_sub(1),
303 make_cell(chars.bottom_left),
304 );
305 }
306
307 if rect.width > 1 && rect.height > 1 {
308 self.set(
309 rect.right().saturating_sub(1),
310 rect.bottom().saturating_sub(1),
311 make_cell(chars.bottom_right),
312 );
313 }
314 }
315
316 fn draw_box(&mut self, rect: Rect, chars: BorderChars, border_cell: Cell, fill_cell: Cell) {
317 if rect.is_empty() {
318 return;
319 }
320
321 if rect.width > 2 && rect.height > 2 {
323 let inner = Rect::new(
324 rect.x.saturating_add(1),
325 rect.y.saturating_add(1),
326 rect.width - 2,
327 rect.height - 2,
328 );
329 self.fill(inner, fill_cell);
330 }
331
332 self.draw_border(rect, chars, border_cell);
334 }
335
336 fn paint_area(
337 &mut self,
338 rect: Rect,
339 fg: Option<crate::cell::PackedRgba>,
340 bg: Option<crate::cell::PackedRgba>,
341 ) {
342 for y in rect.y..rect.bottom() {
343 for x in rect.x..rect.right() {
344 if let Some(cell) = self.get_mut(x, y) {
345 if let Some(fg_color) = fg {
346 cell.fg = fg_color;
347 }
348 if let Some(bg_color) = bg {
349 cell.bg = bg_color;
350 }
351 }
352 }
353 }
354 }
355}
356
357#[cfg(test)]
358mod tests {
359 use super::*;
360 use crate::cell::PackedRgba;
361
362 fn char_at(buf: &Buffer, x: u16, y: u16) -> Option<char> {
365 buf.get(x, y).and_then(|c| {
366 if c.is_empty() {
367 None
368 } else {
369 c.content.as_char()
370 }
371 })
372 }
373
374 #[test]
377 fn horizontal_line_basic() {
378 let mut buf = Buffer::new(10, 1);
379 let cell = Cell::from_char('─');
380 buf.draw_horizontal_line(2, 0, 5, cell);
381 assert_eq!(char_at(&buf, 1, 0), None);
382 assert_eq!(char_at(&buf, 2, 0), Some('─'));
383 assert_eq!(char_at(&buf, 6, 0), Some('─'));
384 assert_eq!(char_at(&buf, 7, 0), None);
385 }
386
387 #[test]
388 fn horizontal_line_zero_width() {
389 let mut buf = Buffer::new(10, 1);
390 buf.draw_horizontal_line(0, 0, 0, Cell::from_char('x'));
391 assert!(buf.get(0, 0).unwrap().is_empty());
393 }
394
395 #[test]
396 fn horizontal_line_clipped_by_scissor() {
397 let mut buf = Buffer::new(10, 1);
398 buf.push_scissor(Rect::new(0, 0, 3, 1));
399 buf.draw_horizontal_line(0, 0, 10, Cell::from_char('x'));
400 assert_eq!(char_at(&buf, 0, 0), Some('x'));
401 assert_eq!(char_at(&buf, 2, 0), Some('x'));
402 assert!(buf.get(3, 0).unwrap().is_empty());
404 }
405
406 #[test]
409 fn vertical_line_basic() {
410 let mut buf = Buffer::new(1, 10);
411 let cell = Cell::from_char('│');
412 buf.draw_vertical_line(0, 1, 4, cell);
413 assert!(buf.get(0, 0).unwrap().is_empty());
414 assert_eq!(char_at(&buf, 0, 1), Some('│'));
415 assert_eq!(char_at(&buf, 0, 4), Some('│'));
416 assert!(buf.get(0, 5).unwrap().is_empty());
417 }
418
419 #[test]
420 fn vertical_line_zero_height() {
421 let mut buf = Buffer::new(1, 10);
422 buf.draw_vertical_line(0, 0, 0, Cell::from_char('x'));
423 assert!(buf.get(0, 0).unwrap().is_empty());
424 }
425
426 #[test]
429 fn rect_filled() {
430 let mut buf = Buffer::new(5, 5);
431 let cell = Cell::from_char('█');
432 buf.draw_rect_filled(Rect::new(1, 1, 3, 3), cell);
433 assert_eq!(char_at(&buf, 1, 1), Some('█'));
435 assert_eq!(char_at(&buf, 3, 3), Some('█'));
436 assert!(buf.get(0, 0).unwrap().is_empty());
438 assert!(buf.get(4, 4).unwrap().is_empty());
439 }
440
441 #[test]
442 fn rect_filled_empty() {
443 let mut buf = Buffer::new(5, 5);
444 buf.draw_rect_filled(Rect::new(0, 0, 0, 0), Cell::from_char('x'));
445 assert!(buf.get(0, 0).unwrap().is_empty());
446 }
447
448 #[test]
451 fn rect_outline_basic() {
452 let mut buf = Buffer::new(5, 5);
453 let cell = Cell::from_char('#');
454 buf.draw_rect_outline(Rect::new(0, 0, 5, 5), cell);
455
456 assert_eq!(char_at(&buf, 0, 0), Some('#'));
458 assert_eq!(char_at(&buf, 4, 0), Some('#'));
459 assert_eq!(char_at(&buf, 0, 4), Some('#'));
460 assert_eq!(char_at(&buf, 4, 4), Some('#'));
461
462 assert_eq!(char_at(&buf, 2, 0), Some('#'));
464 assert_eq!(char_at(&buf, 0, 2), Some('#'));
465
466 assert!(buf.get(2, 2).unwrap().is_empty());
468 }
469
470 #[test]
471 fn rect_outline_1x1() {
472 let mut buf = Buffer::new(5, 5);
473 buf.draw_rect_outline(Rect::new(1, 1, 1, 1), Cell::from_char('o'));
474 assert_eq!(char_at(&buf, 1, 1), Some('o'));
475 }
476
477 #[test]
478 fn rect_outline_2x2() {
479 let mut buf = Buffer::new(5, 5);
480 buf.draw_rect_outline(Rect::new(0, 0, 2, 2), Cell::from_char('#'));
481 assert_eq!(char_at(&buf, 0, 0), Some('#'));
482 assert_eq!(char_at(&buf, 1, 0), Some('#'));
483 assert_eq!(char_at(&buf, 0, 1), Some('#'));
484 assert_eq!(char_at(&buf, 1, 1), Some('#'));
485 }
486
487 #[test]
490 fn print_text_basic() {
491 let mut buf = Buffer::new(20, 1);
492 let cell = Cell::from_char(' '); let end_x = buf.print_text(2, 0, "Hello", cell);
494 assert_eq!(char_at(&buf, 2, 0), Some('H'));
495 assert_eq!(char_at(&buf, 3, 0), Some('e'));
496 assert_eq!(char_at(&buf, 6, 0), Some('o'));
497 assert_eq!(end_x, 7);
498 }
499
500 #[test]
501 fn print_text_preserves_style() {
502 let mut buf = Buffer::new(10, 1);
503 let cell = Cell::from_char(' ')
504 .with_fg(PackedRgba::rgb(255, 0, 0))
505 .with_bg(PackedRgba::rgb(0, 0, 255));
506 buf.print_text(0, 0, "AB", cell);
507 let a = buf.get(0, 0).unwrap();
508 assert_eq!(a.fg, PackedRgba::rgb(255, 0, 0));
509 assert_eq!(a.bg, PackedRgba::rgb(0, 0, 255));
510 }
511
512 #[test]
513 fn print_text_clips_at_buffer_edge() {
514 let mut buf = Buffer::new(5, 1);
515 let end_x = buf.print_text(0, 0, "Hello World", Cell::from_char(' '));
516 assert_eq!(char_at(&buf, 4, 0), Some('o'));
517 assert_eq!(end_x, 5);
518 }
519
520 #[test]
521 fn print_text_clipped_stops_at_max_x() {
522 let mut buf = Buffer::new(20, 1);
523 let end_x = buf.print_text_clipped(0, 0, "Hello World", Cell::from_char(' '), 5);
524 assert_eq!(char_at(&buf, 4, 0), Some('o'));
525 assert_eq!(end_x, 5);
526 assert!(buf.get(5, 0).unwrap().is_empty());
528 }
529
530 #[test]
531 fn print_text_wide_chars() {
532 let mut buf = Buffer::new(10, 1);
533 let end_x = buf.print_text(0, 0, "AB", Cell::from_char(' '));
534 assert_eq!(end_x, 2);
536 assert_eq!(char_at(&buf, 0, 0), Some('A'));
537 assert_eq!(char_at(&buf, 1, 0), Some('B'));
538 }
539
540 #[test]
541 fn print_text_wide_char_clipped() {
542 let mut buf = Buffer::new(10, 1);
543 let end_x = buf.print_text_clipped(4, 0, "中", Cell::from_char(' '), 5);
545 assert_eq!(end_x, 4);
547 }
548
549 #[test]
550 fn print_text_empty_string() {
551 let mut buf = Buffer::new(10, 1);
552 let end_x = buf.print_text(0, 0, "", Cell::from_char(' '));
553 assert_eq!(end_x, 0);
554 }
555
556 #[test]
559 fn draw_border_square() {
560 let mut buf = Buffer::new(5, 3);
561 buf.draw_border(
562 Rect::new(0, 0, 5, 3),
563 BorderChars::SQUARE,
564 Cell::from_char(' '),
565 );
566
567 assert_eq!(char_at(&buf, 0, 0), Some('┌'));
569 assert_eq!(char_at(&buf, 4, 0), Some('┐'));
570 assert_eq!(char_at(&buf, 0, 2), Some('└'));
571 assert_eq!(char_at(&buf, 4, 2), Some('┘'));
572
573 assert_eq!(char_at(&buf, 1, 0), Some('─'));
575 assert_eq!(char_at(&buf, 2, 0), Some('─'));
576 assert_eq!(char_at(&buf, 3, 0), Some('─'));
577
578 assert_eq!(char_at(&buf, 0, 1), Some('│'));
580 assert_eq!(char_at(&buf, 4, 1), Some('│'));
581
582 assert!(buf.get(2, 1).unwrap().is_empty());
584 }
585
586 #[test]
587 fn draw_border_rounded() {
588 let mut buf = Buffer::new(4, 3);
589 buf.draw_border(
590 Rect::new(0, 0, 4, 3),
591 BorderChars::ROUNDED,
592 Cell::from_char(' '),
593 );
594 assert_eq!(char_at(&buf, 0, 0), Some('╭'));
595 assert_eq!(char_at(&buf, 3, 0), Some('╮'));
596 assert_eq!(char_at(&buf, 0, 2), Some('╰'));
597 assert_eq!(char_at(&buf, 3, 2), Some('╯'));
598 }
599
600 #[test]
601 fn draw_border_1x1() {
602 let mut buf = Buffer::new(5, 5);
603 buf.draw_border(
604 Rect::new(1, 1, 1, 1),
605 BorderChars::SQUARE,
606 Cell::from_char(' '),
607 );
608 assert_eq!(char_at(&buf, 1, 1), Some('┌'));
610 }
611
612 #[test]
613 fn draw_border_2x2() {
614 let mut buf = Buffer::new(5, 5);
615 buf.draw_border(
616 Rect::new(0, 0, 2, 2),
617 BorderChars::SQUARE,
618 Cell::from_char(' '),
619 );
620 assert_eq!(char_at(&buf, 0, 0), Some('┌'));
621 assert_eq!(char_at(&buf, 1, 0), Some('┐'));
622 assert_eq!(char_at(&buf, 0, 1), Some('└'));
623 assert_eq!(char_at(&buf, 1, 1), Some('┘'));
624 }
625
626 #[test]
627 fn draw_border_empty_rect() {
628 let mut buf = Buffer::new(5, 5);
629 buf.draw_border(
630 Rect::new(0, 0, 0, 0),
631 BorderChars::SQUARE,
632 Cell::from_char(' '),
633 );
634 assert!(buf.get(0, 0).unwrap().is_empty());
636 }
637
638 #[test]
639 fn draw_border_preserves_style() {
640 let mut buf = Buffer::new(5, 3);
641 let cell = Cell::from_char(' ')
642 .with_fg(PackedRgba::rgb(0, 255, 0))
643 .with_bg(PackedRgba::rgb(0, 0, 128));
644 buf.draw_border(Rect::new(0, 0, 5, 3), BorderChars::SQUARE, cell);
645
646 let corner = buf.get(0, 0).unwrap();
647 assert_eq!(corner.fg, PackedRgba::rgb(0, 255, 0));
648 assert_eq!(corner.bg, PackedRgba::rgb(0, 0, 128));
649
650 let edge = buf.get(2, 0).unwrap();
651 assert_eq!(edge.fg, PackedRgba::rgb(0, 255, 0));
652 }
653
654 #[test]
655 fn draw_border_clipped_by_scissor() {
656 let mut buf = Buffer::new(10, 5);
657 buf.push_scissor(Rect::new(0, 0, 3, 3));
658 buf.draw_border(
659 Rect::new(0, 0, 6, 4),
660 BorderChars::SQUARE,
661 Cell::from_char(' '),
662 );
663
664 assert_eq!(char_at(&buf, 0, 0), Some('┌'));
666 assert_eq!(char_at(&buf, 2, 0), Some('─'));
667
668 assert!(buf.get(5, 0).unwrap().is_empty());
670 assert!(buf.get(0, 3).unwrap().is_empty());
671 }
672
673 #[test]
676 fn draw_box_basic() {
677 let mut buf = Buffer::new(5, 4);
678 let border = Cell::from_char(' ').with_fg(PackedRgba::rgb(255, 255, 255));
679 let fill = Cell::from_char('.').with_bg(PackedRgba::rgb(50, 50, 50));
680 buf.draw_box(Rect::new(0, 0, 5, 4), BorderChars::SQUARE, border, fill);
681
682 assert_eq!(char_at(&buf, 0, 0), Some('┌'));
684 assert_eq!(char_at(&buf, 4, 3), Some('┘'));
685
686 assert_eq!(char_at(&buf, 1, 1), Some('.'));
688 assert_eq!(char_at(&buf, 3, 2), Some('.'));
689 assert_eq!(buf.get(2, 1).unwrap().bg, PackedRgba::rgb(50, 50, 50));
690 }
691
692 #[test]
693 fn draw_box_too_small_for_interior() {
694 let mut buf = Buffer::new(5, 5);
695 let border = Cell::from_char(' ');
696 let fill = Cell::from_char('X');
697 buf.draw_box(Rect::new(0, 0, 2, 2), BorderChars::SQUARE, border, fill);
698
699 assert_eq!(char_at(&buf, 0, 0), Some('┌'));
701 assert_eq!(char_at(&buf, 1, 0), Some('┐'));
702 }
703
704 #[test]
705 fn draw_box_empty() {
706 let mut buf = Buffer::new(5, 5);
707 buf.draw_box(
708 Rect::new(0, 0, 0, 0),
709 BorderChars::SQUARE,
710 Cell::from_char(' '),
711 Cell::from_char('.'),
712 );
713 assert!(buf.get(0, 0).unwrap().is_empty());
714 }
715
716 #[test]
719 fn paint_area_sets_colors() {
720 let mut buf = Buffer::new(5, 3);
721 buf.set(1, 1, Cell::from_char('X'));
723 buf.set(2, 1, Cell::from_char('Y'));
724
725 buf.paint_area(
726 Rect::new(0, 0, 5, 3),
727 None,
728 Some(PackedRgba::rgb(30, 30, 30)),
729 );
730
731 assert_eq!(char_at(&buf, 1, 1), Some('X'));
733 assert_eq!(buf.get(1, 1).unwrap().bg, PackedRgba::rgb(30, 30, 30));
735 assert_eq!(buf.get(0, 0).unwrap().bg, PackedRgba::rgb(30, 30, 30));
736 }
737
738 #[test]
739 fn paint_area_sets_fg() {
740 let mut buf = Buffer::new(3, 1);
741 buf.set(0, 0, Cell::from_char('A'));
742
743 buf.paint_area(
744 Rect::new(0, 0, 3, 1),
745 Some(PackedRgba::rgb(200, 100, 50)),
746 None,
747 );
748
749 assert_eq!(buf.get(0, 0).unwrap().fg, PackedRgba::rgb(200, 100, 50));
750 }
751
752 #[test]
753 fn paint_area_empty_rect() {
754 let mut buf = Buffer::new(5, 5);
755 buf.set(0, 0, Cell::from_char('A'));
756 let original_fg = buf.get(0, 0).unwrap().fg;
757
758 buf.paint_area(
759 Rect::new(0, 0, 0, 0),
760 Some(PackedRgba::rgb(255, 0, 0)),
761 None,
762 );
763
764 assert_eq!(buf.get(0, 0).unwrap().fg, original_fg);
766 }
767
768 #[test]
771 fn all_border_presets() {
772 let mut buf = Buffer::new(6, 4);
773 let cell = Cell::from_char(' ');
774 let rect = Rect::new(0, 0, 6, 4);
775
776 for chars in [
777 BorderChars::SQUARE,
778 BorderChars::ROUNDED,
779 BorderChars::DOUBLE,
780 BorderChars::HEAVY,
781 BorderChars::ASCII,
782 ] {
783 buf.clear();
784 buf.draw_border(rect, chars, cell);
785 assert!(buf.get(0, 0).unwrap().content.as_char().is_some());
787 assert!(buf.get(5, 3).unwrap().content.as_char().is_some());
788 }
789 }
790
791 #[test]
794 fn draw_border_then_print_title() {
795 let mut buf = Buffer::new(12, 3);
796 let cell = Cell::from_char(' ');
797
798 buf.draw_border(Rect::new(0, 0, 12, 3), BorderChars::SQUARE, cell);
800
801 buf.print_text(1, 0, "Title", cell);
803
804 assert_eq!(char_at(&buf, 0, 0), Some('┌'));
805 assert_eq!(char_at(&buf, 1, 0), Some('T'));
806 assert_eq!(char_at(&buf, 5, 0), Some('e'));
807 assert_eq!(char_at(&buf, 6, 0), Some('─'));
808 assert_eq!(char_at(&buf, 11, 0), Some('┐'));
809 }
810
811 #[test]
812 fn draw_nested_borders() {
813 let mut buf = Buffer::new(10, 6);
814 let cell = Cell::from_char(' ');
815
816 buf.draw_border(Rect::new(0, 0, 10, 6), BorderChars::DOUBLE, cell);
817 buf.draw_border(Rect::new(1, 1, 8, 4), BorderChars::SQUARE, cell);
818
819 assert_eq!(char_at(&buf, 0, 0), Some('╔'));
821 assert_eq!(char_at(&buf, 9, 5), Some('╝'));
822
823 assert_eq!(char_at(&buf, 1, 1), Some('┌'));
825 assert_eq!(char_at(&buf, 8, 4), Some('┘'));
826 }
827}