mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-27 11:16:03 +00:00
Move LinesIterator
to utils
module
This commit is contained in:
parent
e77512653d
commit
fb10e58188
@ -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;
|
||||
|
@ -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.
|
||||
//!
|
||||
|
@ -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;
|
||||
|
@ -1,4 +1,4 @@
|
||||
//! Module to handle colors and themes in the UI.
|
||||
//! Handle colors and themes in the UI.
|
||||
//!
|
||||
//! # Color palette
|
||||
//!
|
||||
|
135
src/utils.rs
135
src/utils.rs
@ -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();
|
||||
|
@ -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.
|
||||
//!
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user