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 menu;
pub mod direction;
pub mod utils;
// This probably doesn't need to be public?
mod utils;
mod printer;
mod xy;
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.
//!

View File

@ -3,7 +3,7 @@
use std::cmp::min;
use unicode_segmentation::UnicodeSegmentation;
use utils::head_bytes;
use utils::prefix_length;
use backend::Backend;
use B;
@ -47,7 +47,7 @@ impl Printer {
let room = self.size.x - p.x;
// We want the number of CHARACTERS, not bytes.
// (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 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
//!

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`.
//
// Takes non-breakable elements from `iter`, while keeping the
// string width under `width` (and adding the length of `delimiter`
// between each element).
//
// Example:
//
// ```
// let my_text = "blah...";
// // This returns the number of bytes for a prefix of `my_text` that
// // fits within 5 cells.
// head_bytes(my_text.graphemes(true), 5, "");
// ```
pub fn head_bytes<'a, I: Iterator<Item = &'a str>>(iter: I, width: usize,
use unicode_width::UnicodeWidthStr;
use unicode_segmentation::UnicodeSegmentation;
/// Generates rows of text in constrained width.
///
/// Given a long text and a width constraint, it iterates over
/// substrings of the text, each within the constraint.
pub struct LinesIterator<'a> {
content: &'a str,
start: usize,
width: usize,
}
impl<'a> LinesIterator<'a> {
/// 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)
-> usize {
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.
//!

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 button;

View File

@ -2,16 +2,15 @@ use XY;
use With;
use direction::Direction;
use vec::Vec2;
use view::{View, SizeCache};
use view::{SizeCache, View};
use Printer;
use align::*;
use event::*;
use view::ScrollBase;
use utils::head_bytes;
use utils::{Row, LinesIterator};
use unicode_width::UnicodeWidthStr;
use unicode_segmentation::UnicodeSegmentation;
/// A simple view showing a fixed text
@ -30,13 +29,6 @@ pub struct TextView {
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.
fn strip_last_newline(content: &str) -> &str {
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 {
fn draw(&self, printer: &Printer) {