Move LinesIterator to utils module

This commit is contained in:
Alexandre Bury 2016-07-28 23:05:08 -07:00
parent e77512653d
commit fb10e58188
8 changed files with 128 additions and 104 deletions

View File

@ -82,9 +82,9 @@ pub mod theme;
pub mod align; pub mod align;
pub mod menu; pub mod menu;
pub mod direction; pub mod direction;
pub mod utils;
// This probably doesn't need to be public? // This probably doesn't need to be public?
mod utils;
mod printer; mod printer;
mod xy; mod xy;
mod with; mod with;

View File

@ -1,4 +1,4 @@
//! Module to build menus. //! Build menu trees.
//! //!
//! Menus are a way to arrange many actions in groups of more manageable size. //! Menus are a way to arrange many actions in groups of more manageable size.
//! //!

View File

@ -3,7 +3,7 @@
use std::cmp::min; use std::cmp::min;
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
use utils::head_bytes; use utils::prefix_length;
use backend::Backend; use backend::Backend;
use B; use B;
@ -47,7 +47,7 @@ impl Printer {
let room = self.size.x - p.x; let room = self.size.x - p.x;
// We want the number of CHARACTERS, not bytes. // We want the number of CHARACTERS, not bytes.
// (Actually we want the "width" of the string, see unicode-width) // (Actually we want the "width" of the string, see unicode-width)
let prefix_len = head_bytes(text.graphemes(true), room, ""); let prefix_len = prefix_length(text.graphemes(true), room, "");
let text = &text[..prefix_len]; let text = &text[..prefix_len];
let p = p + self.offset; let p = p + self.offset;

View File

@ -1,4 +1,4 @@
//! Module to handle colors and themes in the UI. //! Handle colors and themes in the UI.
//! //!
//! # Color palette //! # Color palette
//! //!

View File

@ -1,20 +1,123 @@
use unicode_width::UnicodeWidthStr; //! Toolbox to make text layout easier.
// Computes a sub-string length that fits in the given `width`. use unicode_width::UnicodeWidthStr;
// use unicode_segmentation::UnicodeSegmentation;
// Takes non-breakable elements from `iter`, while keeping the
// string width under `width` (and adding the length of `delimiter` /// Generates rows of text in constrained width.
// between each element). ///
// /// Given a long text and a width constraint, it iterates over
// Example: /// substrings of the text, each within the constraint.
// pub struct LinesIterator<'a> {
// ``` content: &'a str,
// let my_text = "blah..."; start: usize,
// // This returns the number of bytes for a prefix of `my_text` that width: usize,
// // fits within 5 cells. }
// head_bytes(my_text.graphemes(true), 5, "");
// ``` impl<'a> LinesIterator<'a> {
pub fn head_bytes<'a, I: Iterator<Item = &'a str>>(iter: I, width: usize, /// Returns a new `LinesIterator` on `content`.
///
/// Yields rows of `width` cells or less.
pub fn new(content: &'a str, width: usize) -> Self {
LinesIterator {
content: content,
width: width,
start: 0,
}
}
}
/// Represents a row of text within a `String`.
///
/// A row is made of an offset into a parent `String` and a length.
/// The corresponding substring should take `width` cells when printed.
pub struct Row {
/// Beginning of the row in the parent `String`.
pub start: usize,
/// Length of the row, in bytes.
pub end: usize,
/// Width of the row, in cells.
pub width: usize,
}
impl<'a> Iterator for LinesIterator<'a> {
type Item = Row;
fn next(&mut self) -> Option<Row> {
if self.start >= self.content.len() {
// This is the end.
return None;
}
let start = self.start;
let content = &self.content[self.start..];
let next = content.find('\n').unwrap_or(content.len());
let content = &content[..next];
let line_width = content.width();
if line_width <= self.width {
// We found a newline before the allowed limit.
// Break early.
self.start += next + 1;
return Some(Row {
start: start,
end: next + start,
width: line_width,
});
}
// Keep adding indivisible tokens
let prefix_length =
match prefix_length(content.split(' '), self.width, " ") {
0 => prefix_length(content.graphemes(true), self.width, ""),
other => {
self.start += 1;
other
}
};
if prefix_length == 0 {
// This mean we can't even get a single char?
// Sucks. Let's bail.
return None;
}
self.start += prefix_length;
Some(Row {
start: start,
end: start + prefix_length,
width: self.width,
})
}
}
/// Computes a sub-string length that fits in the given `width`.
///
/// Takes non-breakable elements from `iter`, while keeping the
/// string width under `width` (and adding the length of `delimiter`
/// between each element).
///
/// Given `total_text = iter.collect().join(delimiter)`, the result
/// is the length of the longest prefix of `width` or less cells,
/// without breaking inside an element.
///
/// Example:
///
/// ```
/// # extern crate cursive;
/// extern crate unicode_segmentation;
/// use unicode_segmentation::UnicodeSegmentation;
///
/// # use cursive::utils::prefix_length;
/// # fn main() {
/// let my_text = "blah...";
/// // This returns the number of bytes for a prefix of `my_text` that
/// // fits within 5 cells.
/// prefix_length(my_text.graphemes(true), 5, "");
/// # }
/// ```
pub fn prefix_length<'a, I: Iterator<Item = &'a str>>(iter: I, width: usize,
delimiter: &str) delimiter: &str)
-> usize { -> usize {
let delimiter_width = delimiter.width(); let delimiter_width = delimiter.width();

View File

@ -1,4 +1,4 @@
//! Define the base elements required to build views. //! Base elements required to build views.
//! //!
//! Views are the main building blocks of your UI. //! Views are the main building blocks of your UI.
//! //!

View File

@ -1,4 +1,4 @@
//! Defines various views to use when creating the layout. //! Various views to use when creating the layout.
mod box_view; mod box_view;
mod button; mod button;

View File

@ -2,16 +2,15 @@ use XY;
use With; use With;
use direction::Direction; use direction::Direction;
use vec::Vec2; use vec::Vec2;
use view::{View, SizeCache}; use view::{SizeCache, View};
use Printer; use Printer;
use align::*; use align::*;
use event::*; use event::*;
use view::ScrollBase; use view::ScrollBase;
use utils::head_bytes; use utils::{Row, LinesIterator};
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use unicode_segmentation::UnicodeSegmentation;
/// A simple view showing a fixed text /// A simple view showing a fixed text
@ -30,13 +29,6 @@ pub struct TextView {
width: Option<usize>, width: Option<usize>,
} }
// Subset of the main content representing a row on the display.
struct Row {
start: usize,
end: usize,
width: usize,
}
// If the last character is a newline, strip it. // If the last character is a newline, strip it.
fn strip_last_newline(content: &str) -> &str { fn strip_last_newline(content: &str) -> &str {
if !content.is_empty() && content.chars().last().unwrap() == '\n' { if !content.is_empty() && content.chars().last().unwrap() == '\n' {
@ -182,77 +174,6 @@ impl TextView {
} }
} }
// Given a multiline string, and a given maximum width,
// iterates on the computed rows.
struct LinesIterator<'a> {
content: &'a str,
start: usize,
width: usize,
}
impl<'a> LinesIterator<'a> {
// Start an iterator on the given content.
fn new(content: &'a str, width: usize) -> Self {
LinesIterator {
content: content,
width: width,
start: 0,
}
}
}
impl<'a> Iterator for LinesIterator<'a> {
type Item = Row;
fn next(&mut self) -> Option<Row> {
if self.start >= self.content.len() {
// This is the end.
return None;
}
let start = self.start;
let content = &self.content[self.start..];
let next = content.find('\n').unwrap_or(content.len());
let content = &content[..next];
let line_width = content.width();
if line_width <= self.width {
// We found a newline before the allowed limit.
// Break early.
self.start += next + 1;
return Some(Row {
start: start,
end: next + start,
width: line_width,
});
}
// Keep adding indivisible tokens
let head_bytes =
match head_bytes(content.split(' '), self.width, " ") {
0 => head_bytes(content.graphemes(true), self.width, ""),
other => {
self.start += 1;
other
}
};
if head_bytes == 0 {
// This mean we can't even get a single char?
// Sucks. Let's bail.
return None;
}
self.start += head_bytes;
Some(Row {
start: start,
end: start + head_bytes,
width: self.width,
})
}
}
impl View for TextView { impl View for TextView {
fn draw(&self, printer: &Printer) { fn draw(&self, printer: &Printer) {