use std::ops::Range;
pub trait TextWrapper {
fn push(&mut self, c: char, advance: f32);
fn finish(&mut self);
}
pub trait LineSink {
fn push_line(&mut self, range: Range<usize>, width: f32);
fn max_width(&self) -> f32;
}
fn is_newline(c: char) -> bool {
c == '\n' || c == '\r'
}
pub struct NoWrap<S> {
sink: S,
start: usize,
position: usize,
width: f32,
}
impl<S> NoWrap<S> {
pub fn new(sink: S) -> Self {
NoWrap {
sink,
start: 0,
position: 0,
width: 0.0,
}
}
}
impl<S: LineSink> TextWrapper for NoWrap<S> {
fn push(&mut self, c: char, advance: f32) {
if is_newline(c) {
self.sink.push_line(self.start..self.position, self.width);
self.start = self.position + 1; self.position += 1;
self.width = 0.0;
} else {
self.position += 1;
self.width += advance;
}
}
fn finish(&mut self) {
self.sink.push_line(self.start..self.position, self.width);
}
}
pub struct LetterWrap<S> {
sink: S,
start: usize,
position: usize,
width: f32,
}
impl<S> LetterWrap<S> {
pub fn new(sink: S) -> Self {
LetterWrap {
sink,
start: 0,
position: 0,
width: 0.0,
}
}
}
impl<S: LineSink> TextWrapper for LetterWrap<S> {
fn push(&mut self, c: char, advance: f32) {
if self.position != self.start && self.width + advance > self.sink.max_width() {
self.sink.push_line(self.start..self.position, self.width);
self.start = self.position;
self.width = 0.0;
}
self.position += 1;
self.width += advance;
if is_newline(c) {
self.sink.push_line(self.start..self.position, self.width);
self.start = self.position; self.width = 0.0;
}
}
fn finish(&mut self) {
if self.start != self.position {
self.sink.push_line(self.start..self.position, self.width);
}
}
}
pub struct WordWrap<S> {
sink: S,
start: usize,
position: usize,
width: f32,
word_start: usize,
word_width: f32,
}
impl<S> WordWrap<S> {
pub fn new(sink: S) -> Self {
WordWrap {
sink,
start: 0,
position: 0,
width: 0.0,
word_start: 0,
word_width: 0.0,
}
}
}
impl<S: LineSink> TextWrapper for WordWrap<S> {
fn push(&mut self, c: char, advance: f32) {
if self.position != self.start && self.width + advance > self.sink.max_width() {
if self.start < self.word_start {
self.sink
.push_line(self.start..self.word_start, self.width - self.word_width);
self.start = self.word_start;
self.width = self.word_width;
} else {
self.sink.push_line(self.start..self.position, self.width);
self.start = self.position;
self.width = 0.0;
}
}
self.position += 1;
self.width += advance;
if is_newline(c) {
self.sink.push_line(self.start..self.position, self.width);
self.start = self.position;
self.width = 0.0;
self.word_start = self.position;
self.word_width = 0.0;
} else if c.is_whitespace() {
self.word_start = self.position;
self.word_width = 0.0;
} else {
self.word_width += advance;
}
}
fn finish(&mut self) {
self.sink.push_line(self.start..self.position, self.width);
}
}
#[cfg(test)]
mod tests {
use super::*;
struct Sink(f32, Vec<(Range<usize>, f32)>);
impl Sink {
fn new(width: f32) -> Self {
Self(width, Vec::default())
}
fn result(&self) -> &[(Range<usize>, f32)] {
self.1.as_slice()
}
}
impl LineSink for Sink {
fn push_line(&mut self, range: Range<usize>, width: f32) {
self.1.push((range, width));
}
fn max_width(&self) -> f32 {
self.0
}
}
fn wrap<W: TextWrapper>(wrap: &mut W, text: &str) {
for c in text.chars() {
wrap.push(c, 1.0);
}
wrap.finish();
}
#[test]
fn one_line_no_wrap() {
let mut w = NoWrap::new(Sink::new(10.0));
wrap(&mut w, "One line");
assert_eq!(w.sink.result(), &[(0..8, 8.0)]);
}
#[test]
fn two_line_no_wrap() {
let mut w = NoWrap::new(Sink::new(10.0));
wrap(&mut w, "Two\nline");
assert_eq!(w.sink.result(), &[(0..3, 3.0), (4..8, 4.0)]);
}
#[test]
fn one_line_letter() {
let mut w = LetterWrap::new(Sink::new(8.0));
wrap(&mut w, "One line");
assert_eq!(w.sink.result(), &[(0..8, 8.0)]);
}
#[test]
fn two_line_letter() {
let mut w = LetterWrap::new(Sink::new(8.0));
wrap(&mut w, "Two line!");
assert_eq!(w.sink.result(), &[(0..8, 8.0), (8..9, 1.0)]);
}
#[test]
fn new_line_letter() {
let mut w = LetterWrap::new(Sink::new(8.0));
wrap(&mut w, "Two\nline");
assert_eq!(w.sink.result(), &[(0..4, 4.0), (4..8, 4.0)]);
}
#[test]
fn one_line_word() {
let mut w = WordWrap::new(Sink::new(8.0));
wrap(&mut w, "One line");
assert_eq!(w.sink.result(), &[(0..8, 8.0)]);
}
#[test]
fn two_line_word() {
let mut w = WordWrap::new(Sink::new(8.0));
wrap(&mut w, "Two line!");
assert_eq!(w.sink.result(), &[(0..4, 4.0), (4..9, 5.0)]);
}
#[test]
fn new_line_word() {
let mut w = WordWrap::new(Sink::new(8.0));
wrap(&mut w, "Two\nline");
assert_eq!(w.sink.result(), &[(0..4, 4.0), (4..8, 4.0)]);
}
}