Better UTF-8 support in views

EditView, TextView, Dialog and Button should now properly display and
layout non-ascii text.
The lorem example now includes greek text to show this.
This commit is contained in:
Alexandre Bury 2015-05-28 17:32:28 -07:00
parent ae93b68c05
commit 6c6f929430
5 changed files with 64 additions and 20 deletions

View File

@ -17,3 +17,23 @@ Praesent dui lectus, commodo eget nulla ut, maximus facilisis ligula. Aliquam ul
Etiam diam neque, dictum in eros ac, lacinia gravida orci. Cras in nisi augue. Fusce condimentum vestibulum nisl convallis lacinia. Mauris ligula diam, facilisis quis nulla ut, luctus feugiat eros. Duis ac consequat nisl. Nulla facilisi. Integer euismod mauris a feugiat gravida. Nulla consectetur est vitae lectus semper porttitor. Cras pellentesque tincidunt lacus, id sagittis lectus tincidunt eu. Mauris pellentesque lobortis aliquet. Mauris nec est bibendum, cursus metus eget, maximus nunc. Donec fermentum eros quis dolor imperdiet accumsan.
Sed vitae rhoncus velit. Proin eu luctus libero. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean imperdiet diam vitae eros vehicula mattis. Sed auctor erat et sapien mattis, sit amet faucibus risus tempus. Praesent ut commodo dui. Pellentesque et sodales purus. Nullam mollis sed urna eget aliquet.
Μελιορε ιμπερδιετ αδ φιξ, δωλορε θριθανι ασυσαμυς ευ πρι. Ευ ναμ αδχυς σριπτορεμ. Δισερετ ωπωρθεαθ δεφινιτιωνες υθ σεα, φιδιτ φιθαε κυαλισκυε ετ εσθ, ιυς δισπυθανδο ινστρυσθιορ εα. Νε σεδ ρεγιονε οσυρρερεθ.
Ατ συμο υλλυμ ηομερω φελ, αεκυε πραεσενθ ρεπυδιαρε ιυς αν. Εσθ δισπυθανδο μεδιοσριθαθεμ ιν, φις ευ δισιθ αυδιαμ μενθιτυμ. Νο περ δισαμ τρασθατος, ιδ ιγνωθα ποπυλω ειρμωδ ιυς, ιδ δισαντ δεσερυισε μεα. Αδ παρτεμ φεριθυς πρω.
Φιδιτ δοσθυς ρεπρεχενδυντ νες νο, ιλλυδ αθκυι εξπλισαρι υθ εως, πρω ερος φαλλι ινιμισυς νο. Νο φιμ νυλλα σεντεντιαε συσιπιαντυρ, αδ νοσθρω ιμπεδιτ φασιλις σεδ, αδ ευμ δισο προμπτα ιμπερδιετ. Ιδ ηις μαγνα ωμιτθαμ θχεωπηραστυς, οδιο παθριοκυε ηας ιν, εξ ιυς ινθεγρε αλθερυμ περσεσυτι. Ιδ ανιμαλ τεμποριβυς δεφινιτιωνες φιξ. Σεδ ευ πλασεραθ σωνσεπθαμ, αδ κυωδ ειυς σομπλεσθιθυρ ιυς, εσεντ δολορες ετ κυι.
Νο σεδ σολετ σωνσεθεθυρ αδιπισινγ. Νο σιθ φαστιδιι περφεσθο σαπιενθεμ, εα ινερμις ινδοστυμ δισεντιας φελ, υσυ ει υθροκυε μαλυισετ. Μει κυαεστιο μνεσαρσχυμ νο. Εα δομινγ αεθερνο αλθερυμ δυο, ευ σεα σασε μολλις, λωρεμ ωμνιυμ ιισκυε σεδ νε.
Μεα θε δισαθ φαλλι οφφισιις. Ναμ δυις ιριυρε αδ. Φασιλις σεθερος αλβυσιυς ατ μει, μελ υθ ρεπυδιαρε ασεντιορ ινσιδεριντ. Σαεπε πρωβατυς ηις αν, φευγιαθ υλλαμσορπερ ατ μει, ει κυο ριδενς αππαρεατ διγνισιμ. Αλιενυμ ελαβοραρετ σονσλυσιονεμκυε κυι αν, παυλω μεδιοσρεμ υσυ συ, συ γραεσι λυπταθυμ σωνσεκυαθ σιθ. Σιθ θε ηαβεο φοσιβυς σονστιθυαμ, περ ετ φιθαε εξερσι μολλις.
Ναμ αθκυι αυδιαμ αδ. Ευ κυαεστιο φυλπυτατε εσθ. Υθ σολυτα σομμοδο δετρασθο δυο, εα σιθ δελισατα σωνσεπθαμ. Ευμ υτιναμ ασομμοδαρε ει, σολεατ ιριυρε ιν συμ. Φιδερερ σεθερος δεφινιεβας ιυς θε.
Δεσωρε πωσιθ φιφενδυμ εα φιμ, κυωδ σονσυλατυ σοτιδιεκυε φελ αδ. Ιδ ναμ δισαθ ταντας, δισαντ φασιλις ηις υθ. Κυεμ εραντ δισερε σεδ ει, εα φιρθυθε ωπωρθεαθ σενσεριτ ευμ. Μελ θαλε αππελλανθυρ ιδ. Ιδ κυι σαλε περσιπιθ. Ιδ σεδ λαβιθυρ σωνσεκυαθ φορενσιβυς, εξ στετ δελενιτι σιθ. Αδ μοδυς νοφυμ σωνσλυδαθυρκυε συμ, φιμ ευ χινς δεβετ, φιξ ιν αλια ριδενς παθριοκυε.
Δυο απεριαμ σομπλεσθιθυρ ιδ, νο μανδαμυς παρτιενδω συμ, φιδιτ λιβρις φαβυλας μεα νε. Θαλε μυτατ πωνδερυμ ευ περ, ριδενς οπωρθερε περ αδ, εα φασερ ερρωρ πρω. Υσυ ατ φελιτ λαορεεθ. Μεα πορρω επισυρι εξπλισαρι ιδ, βωνορυμ δολωρυμ νυσκυαμ εαμ αν, παυλω λυσιλιυς ερροριβυς μεα υθ. Ει φελ αμετ νιηιλ σριπτορεμ, σοπιωσαε ινφενιρε σεα νο. Νε σολεατ περπετυα υσυ. Εα πρω δελεσθυς θχεωπηραστυς, θαθιων σεθερος φις θε, ιυς ετ σαεπε φολυτπατ μαιεσθατις.
Ιυς ιν διαμ μαλυισετ σορρυμπιθ. Οδιο σπλενδιδε σιθ ατ, νες εραθ λατινε ατ. Νες εξ ποσιμ δεσερυισε. Ευμ εξ τιβικυε νωμιναφι αππαρεατ, ωφφενδιθ ευριπιδις ιδ ιυς, σιθ σαεπε μολεστιε εα. Φις χαρυμ κυοδσι ευ. Περτινασια λιβεραφισε σεα ιδ. Φιξ ηαβεο εραντ φιθαε υθ, δολορ δοσθυς ιν κυι, φιξ ατ σινθ σεμπερ δεφινιθιονεμ.
Ταντας νομινατι σωνσεθεθυρ ει ναμ. Ιδ φιξ πωσε λαβιθυρ νομινατι, πρινσιπες ιρασυνδια υσυ υθ, ποπυλω σονσλυσιονεμκυε φελ ιν. Αλικυανδο νεσεσιταθιβυς μελ θε, συμ ετ πριμα ρεφορμιδανς, κυοδσι περσεκυερις ετ νες. Εκυιδεμ φιφενδω πρωβατυς ηας ετ. Νε σεδ ταντας σολυτα ανιμαλ.

View File

@ -41,7 +41,7 @@ impl View for Button {
fn get_min_size(&self, _: SizeRequest) -> Vec2 {
// Meh. Fixed size we are.
Vec2::new(2 + self.label.len(), 1)
Vec2::new(2 + self.label.chars().count(), 1)
}
fn on_event(&mut self, event: Event) -> EventResult {

View File

@ -108,9 +108,10 @@ impl View for Dialog {
printer.print_box(Vec2::new(0,0), printer.size);
if self.title.len() > 0 {
let x = (printer.size.x - self.title.len()) / 2;
let len = self.title.chars().count();
let x = (printer.size.x - len) / 2;
printer.print((x-2,0), "");
printer.print((x+self.title.len(),0), "");
printer.print((x+len,0), "");
printer.with_color(color::TITLE_PRIMARY, |p| p.print((x,0), &self.title));
}
@ -137,7 +138,7 @@ impl View for Dialog {
if self.title.len() > 0 {
// If we have a title, we have to fit it too!
inner_size.x = max(inner_size.x, self.title.len() + 6);
inner_size.x = max(inner_size.x, self.title.chars().count() + 6);
}
inner_size

View File

@ -48,22 +48,31 @@ impl EditView {
}
}
fn remove_char(s: &mut String, cursor: usize) {
let i = match s.char_indices().nth(cursor) {
Some((i,_)) => i,
None => return,
};
s.remove(i);
}
impl View for EditView {
fn draw(&mut self, printer: &Printer, focused: bool) {
// let style = if focused { color::HIGHLIGHT } else { color::HIGHLIGHT_INACTIVE };
let len = self.content.chars().count();
printer.with_color(color::SECONDARY, |printer| {
printer.with_style(ncurses::A_REVERSE(), |printer| {
printer.print((0,0), &self.content);
printer.print_hline((self.content.len(),0), printer.size.x-self.content.len(), '_' as u64);
printer.print_hline((len,0), printer.size.x-len, '_' as u64);
});
// Now print cursor
if focused {
let c = if self.cursor == self.content.len() {
let c = if self.cursor == len {
'_'
} else {
// Get the char from the string... Is it so hard?
self.content.chars().nth(self.cursor).unwrap()
self.content.chars().nth(self.cursor).expect(&format!("Found no char at cursor {} in {}", self.cursor, self.content))
};
printer.print_hline((self.cursor, 0), 1, c as u64);
}
@ -82,17 +91,22 @@ impl View for EditView {
match event {
Event::CharEvent(ch) => {
self.content.insert(self.cursor, ch);
// Find the byte index of the char at self.cursor
match self.content.char_indices().nth(self.cursor) {
None => self.content.push(ch),
Some((i,_)) => self.content.insert(i, ch),
}
self.cursor += 1;
return EventResult::Consumed(None);
},
Event::KeyEvent(key) => match key {
Key::Home => self.cursor = 0,
Key::End => self.cursor = self.content.len(),
Key::End => self.cursor = self.content.chars().count(),
Key::Left if self.cursor > 0 => self.cursor -= 1,
Key::Right if self.cursor < self.content.len() => self.cursor += 1,
Key::Backspace if self.cursor > 0 => { self.cursor -= 1; self.content.remove(self.cursor); },
Key::Del if self.cursor < self.content.len() => { self.content.remove(self.cursor); },
Key::Right if self.cursor < self.content.chars().count() => self.cursor += 1,
Key::Backspace if self.cursor > 0 => { self.cursor -= 1; remove_char(&mut self.content, self.cursor); },
Key::Del if self.cursor < self.content.chars().count() => { remove_char(&mut self.content, self.cursor); },
_ => return EventResult::Ignored,
},

View File

@ -37,7 +37,7 @@ fn get_line_span(line: &str, max_width: usize) -> usize {
// (Or use a common function? Better!)
let mut lines = 1;
let mut length = 0;
for l in line.split(" ").map(|word| word.len()) {
for l in line.split(" ").map(|word| word.chars().count()) {
length += l;
if length > max_width {
length = l;
@ -81,7 +81,8 @@ impl TextView {
// Given the specified height, how many columns do we need to properly display?
fn get_num_cols(&self, max_height: usize) -> usize {
(div_up_usize(self.content.len(), max_height)..self.content.len())
let len = self.content.chars().count();
(div_up_usize(len, max_height)..len)
.find(|w| self.get_num_lines(*w) <= max_height)
.unwrap()
}
@ -93,7 +94,7 @@ impl TextView {
for line in self.content.split("\n") {
height += 1;
max_width = max(max_width, line.len());
max_width = max(max_width, line.chars().count());
}
Vec2::new(max_width, height)
@ -133,7 +134,7 @@ impl <'a> Iterator for LinesIterator<'a> {
let content = &self.content[self.start..];
if let Some(next) = content.find("\n") {
if next <= self.width {
if content[..next].chars().count() <= self.width {
// We found a newline before the allowed limit.
// Break early.
self.start += next+1;
@ -144,7 +145,8 @@ impl <'a> Iterator for LinesIterator<'a> {
}
}
if content.len() <= self.width {
let content_len = content.chars().count();
if content_len <= self.width {
// I thought it would be longer! -- that's what she said :(
self.start += content.len();
return Some(Row{
@ -153,7 +155,14 @@ impl <'a> Iterator for LinesIterator<'a> {
});
}
if let Some(i) = content[..self.width+1].rfind(" ") {
let i = if content_len == self.width+1 {
// We can't look at the index if we're looking at the end of the string
content.len()
} else {
content.char_indices().nth(self.width+1).unwrap().0
};
let substr = &content[..i];
if let Some(i) = substr.rfind(" ") {
// If we have to break, try to find a whitespace for that.
self.start += i+1;
return Some(Row {
@ -200,8 +209,8 @@ impl View for TextView {
match event {
Event::KeyEvent(Key::Up) if self.start_line > 0 => self.start_line -= 1,
Event::KeyEvent(Key::Down) if self.start_line+self.view_height < self.rows.len() => self.start_line += 1,
Event::KeyEvent(Key::PageUp) => self.start_line = min(self.start_line+10, self.rows.len()-self.view_height),
Event::KeyEvent(Key::PageDown) => self.start_line -= min(self.start_line, 10),
Event::KeyEvent(Key::PageDown) => self.start_line = min(self.start_line+10, self.rows.len()-self.view_height),
Event::KeyEvent(Key::PageUp) => self.start_line -= min(self.start_line, 10),
_ => return EventResult::Ignored,
}