Improve wrapping of double-width characters

pull/105/head
Karoline Pauls 6 years ago committed by Florian Dehau
parent 090975481b
commit 1802cf8dbc

@ -3,7 +3,6 @@ use std::fmt;
use std::usize;
use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthStr;
use layout::Rect;
use style::{Color, Modifier, Style};
@ -163,7 +162,10 @@ impl Buffer {
{
let height = lines.len() as u16;
let width = lines.iter().fold(0, |acc, item| {
std::cmp::max(acc, item.as_ref().width() as u16)
std::cmp::max(
acc,
UnicodeSegmentation::graphemes(item.as_ref(), true).count() as u16,
)
});
let mut buffer = Buffer::empty(Rect {
x: 0,

@ -166,7 +166,9 @@ where
y += 1;
continue;
}
if x >= text_area.width {
let token_end_index = x + string.width() as u16 - 1;
let last_column_index = text_area.width - 1;
if token_end_index > last_column_index {
if !self.wrapping {
continue; // Truncate the remainder of the line.
} else {

@ -0,0 +1,111 @@
extern crate failure;
extern crate termion;
extern crate tui;
use tui::backend::TestBackend;
use tui::buffer::Buffer;
use tui::widgets::{Block, Borders, Paragraph, Text, Widget};
use tui::Terminal;
#[test]
fn paragraph_render_single_width() {
let backend = TestBackend::new(20, 10);
let mut terminal = Terminal::new(backend).unwrap();
let s = "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.";
terminal
.draw(|mut f| {
let size = f.size();
let text = [Text::raw(s)];
Paragraph::new(text.iter())
.block(Block::default().borders(Borders::ALL))
.wrap(true)
.render(&mut f, size);
})
.unwrap();
let expected = Buffer::with_lines(vec![
"┌──────────────────┐",
"│The library is bas│",
"│ed on the principl│",
"│e of immediate ren│",
"│dering with interm│",
"│ediate buffers. Th│",
"│is means that at e│",
"│ach new frame you │",
"│should build all w│",
"└──────────────────┘",
]);
assert_eq!(&expected, terminal.backend().buffer());
}
#[test]
fn paragraph_render_double_width() {
let backend = TestBackend::new(10, 10);
let mut terminal = Terminal::new(backend).unwrap();
let s = "コンピュータ上で文字を扱う場合、典型的には文字による通信を行う場合にその両端点では、";
terminal
.draw(|mut f| {
let size = f.size();
let text = [Text::raw(s)];
Paragraph::new(text.iter())
.block(Block::default().borders(Borders::ALL))
.wrap(true)
.render(&mut f, size);
})
.unwrap();
let expected = Buffer::with_lines(vec![
// This is "OK" - these are double-width characters. In terminal each occupies 2 spaces,
// which means the buffer contains a Cell with a full grapheme in it, followed by a vacant
// one. Here however, we have plain text, so each character is visibly followed by a space.
"┌────────┐",
"│コ ン ピ ュ │",
"│ー タ 上 で │",
"│文 字 を 扱 │",
"│う 場 合 、 │",
"│典 型 的 に │",
"│は 文 字 に │",
"│よ る 通 信 │",
"│を 行 う 場 │",
"└────────┘",
]);
assert_eq!(&expected, terminal.backend().buffer());
}
#[test]
fn paragraph_render_mixed_width() {
let backend = TestBackend::new(10, 7);
let mut terminal = Terminal::new(backend).unwrap();
let s = "aコンピュータ上で文字を扱う場合、";
terminal
.draw(|mut f| {
let size = f.size();
let text = [Text::raw(s)];
Paragraph::new(text.iter())
.block(Block::default().borders(Borders::ALL))
.wrap(true)
.render(&mut f, 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.
"│ュ ー タ 上 │",
"│で 文 字 を │",
"│扱 う 場 合 │",
"│、 │",
"└────────┘",
]);
assert_eq!(&expected, terminal.backend().buffer());
}
Loading…
Cancel
Save