| use tui::{ |
| backend::TestBackend, |
| buffer::Buffer, |
| layout::Alignment, |
| text::{Span, Spans, Text}, |
| widgets::{Block, Borders, Paragraph, Wrap}, |
| Terminal, |
| }; |
| |
| const SAMPLE_STRING: &str = "The library is based on the principle of immediate rendering with \ |
| intermediate buffers. This means that at each new frame you should build all widgets that are \ |
| supposed to be part of the UI. While providing a great flexibility for rich and \ |
| interactive UI, this may introduce overhead for highly dynamic content."; |
| |
| #[test] |
| fn widgets_paragraph_can_wrap_its_content() { |
| let test_case = |alignment, expected| { |
| let backend = TestBackend::new(20, 10); |
| let mut terminal = Terminal::new(backend).unwrap(); |
| |
| terminal |
| .draw(|f| { |
| let size = f.size(); |
| let text = vec![Spans::from(SAMPLE_STRING)]; |
| let paragraph = Paragraph::new(text) |
| .block(Block::default().borders(Borders::ALL)) |
| .alignment(alignment) |
| .wrap(Wrap { trim: true }); |
| f.render_widget(paragraph, size); |
| }) |
| .unwrap(); |
| terminal.backend().assert_buffer(&expected); |
| }; |
| |
| test_case( |
| Alignment::Left, |
| Buffer::with_lines(vec![ |
| "┌──────────────────┐", |
| "│The library is │", |
| "│based on the │", |
| "│principle of │", |
| "│immediate │", |
| "│rendering with │", |
| "│intermediate │", |
| "│buffers. This │", |
| "│means that at each│", |
| "└──────────────────┘", |
| ]), |
| ); |
| test_case( |
| Alignment::Right, |
| Buffer::with_lines(vec![ |
| "┌──────────────────┐", |
| "│ The library is│", |
| "│ based on the│", |
| "│ principle of│", |
| "│ immediate│", |
| "│ rendering with│", |
| "│ intermediate│", |
| "│ buffers. This│", |
| "│means that at each│", |
| "└──────────────────┘", |
| ]), |
| ); |
| test_case( |
| Alignment::Center, |
| Buffer::with_lines(vec![ |
| "┌──────────────────┐", |
| "│ The library is │", |
| "│ based on the │", |
| "│ principle of │", |
| "│ immediate │", |
| "│ rendering with │", |
| "│ intermediate │", |
| "│ buffers. This │", |
| "│means that at each│", |
| "└──────────────────┘", |
| ]), |
| ); |
| } |
| |
| #[test] |
| fn widgets_paragraph_renders_double_width_graphemes() { |
| let backend = TestBackend::new(10, 10); |
| let mut terminal = Terminal::new(backend).unwrap(); |
| |
| let s = "コンピュータ上で文字を扱う場合、典型的には文字による通信を行う場合にその両端点では、"; |
| terminal |
| .draw(|f| { |
| let size = f.size(); |
| let text = vec![Spans::from(s)]; |
| let paragraph = Paragraph::new(text) |
| .block(Block::default().borders(Borders::ALL)) |
| .wrap(Wrap { trim: true }); |
| f.render_widget(paragraph, size); |
| }) |
| .unwrap(); |
| |
| let expected = Buffer::with_lines(vec![ |
| "┌────────┐", |
| "│コンピュ│", |
| "│ータ上で│", |
| "│文字を扱│", |
| "│う場合、│", |
| "│典型的に│", |
| "│は文字に│", |
| "│よる通信│", |
| "│を行う場│", |
| "└────────┘", |
| ]); |
| terminal.backend().assert_buffer(&expected); |
| } |
| |
| #[test] |
| fn widgets_paragraph_renders_mixed_width_graphemes() { |
| let backend = TestBackend::new(10, 7); |
| let mut terminal = Terminal::new(backend).unwrap(); |
| |
| let s = "aコンピュータ上で文字を扱う場合、"; |
| terminal |
| .draw(|f| { |
| let size = f.size(); |
| let text = vec![Spans::from(s)]; |
| let paragraph = Paragraph::new(text) |
| .block(Block::default().borders(Borders::ALL)) |
| .wrap(Wrap { trim: true }); |
| f.render_widget(paragraph, size); |
| }) |
| .unwrap(); |
| |
| let expected = Buffer::with_lines(vec![ |
| // The internal width is 8 so only 4 slots for double-width characters. |
| "┌────────┐", |
| "│aコンピ │", // Here we have 1 latin character so only 3 double-width ones can fit. |
| "│ュータ上│", |
| "│で文字を│", |
| "│扱う場合│", |
| "│、 │", |
| "└────────┘", |
| ]); |
| terminal.backend().assert_buffer(&expected); |
| } |
| |
| #[test] |
| fn widgets_paragraph_can_wrap_with_a_trailing_nbsp() { |
| let nbsp: &str = "\u{00a0}"; |
| let line = Spans::from(vec![Span::raw("NBSP"), Span::raw(nbsp)]); |
| let backend = TestBackend::new(20, 3); |
| let mut terminal = Terminal::new(backend).unwrap(); |
| let expected = Buffer::with_lines(vec![ |
| "┌──────────────────┐", |
| "│NBSP\u{00a0} │", |
| "└──────────────────┘", |
| ]); |
| terminal |
| .draw(|f| { |
| let size = f.size(); |
| |
| let paragraph = Paragraph::new(line).block(Block::default().borders(Borders::ALL)); |
| f.render_widget(paragraph, size); |
| }) |
| .unwrap(); |
| terminal.backend().assert_buffer(&expected); |
| } |
| #[test] |
| fn widgets_paragraph_can_scroll_horizontally() { |
| let test_case = |alignment, scroll, expected| { |
| let backend = TestBackend::new(20, 10); |
| let mut terminal = Terminal::new(backend).unwrap(); |
| |
| terminal |
| .draw(|f| { |
| let size = f.size(); |
| let text = Text::from( |
| "段落现在可以水平滚动了!\nParagraph can scroll horizontally!\nShort line", |
| ); |
| let paragraph = Paragraph::new(text) |
| .block(Block::default().borders(Borders::ALL)) |
| .alignment(alignment) |
| .scroll(scroll); |
| f.render_widget(paragraph, size); |
| }) |
| .unwrap(); |
| terminal.backend().assert_buffer(&expected); |
| }; |
| |
| test_case( |
| Alignment::Left, |
| (0, 7), |
| Buffer::with_lines(vec![ |
| "┌──────────────────┐", |
| "│在可以水平滚动了!│", |
| "│ph can scroll hori│", |
| "│ine │", |
| "│ │", |
| "│ │", |
| "│ │", |
| "│ │", |
| "│ │", |
| "└──────────────────┘", |
| ]), |
| ); |
| // only support Alignment::Left |
| test_case( |
| Alignment::Right, |
| (0, 7), |
| Buffer::with_lines(vec![ |
| "┌──────────────────┐", |
| "│段落现在可以水平滚│", |
| "│Paragraph can scro│", |
| "│ Short line│", |
| "│ │", |
| "│ │", |
| "│ │", |
| "│ │", |
| "│ │", |
| "└──────────────────┘", |
| ]), |
| ); |
| } |