mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +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 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;
|
||||||
|
@ -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.
|
||||||
//!
|
//!
|
||||||
|
@ -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;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! Module to handle colors and themes in the UI.
|
//! Handle colors and themes in the UI.
|
||||||
//!
|
//!
|
||||||
//! # Color palette
|
//! # 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`.
|
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();
|
||||||
|
@ -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.
|
||||||
//!
|
//!
|
||||||
|
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user