use {FontSize, Scalar};
use std;
pub use rusttype::{Glyph, GlyphId, GlyphIter, LayoutIter, Scale};
pub use rusttype::gpu_cache::Cache as GlyphCache;
pub mod rt {
pub use rusttype::{Point, Rect, Vector, gpu_cache, point, vector};
}
pub type FontCollection = ::rusttype::FontCollection<'static>;
pub type Font = ::rusttype::Font<'static>;
pub type PositionedGlyph = ::rusttype::PositionedGlyph<'static>;
#[derive(Clone)]
pub struct Lines<'a, I> {
text: &'a str,
ranges: I,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Justify {
Left,
Center,
Right,
}
pub fn height(num_lines: usize, font_size: FontSize, line_spacing: Scalar) -> Scalar {
if num_lines > 0 {
num_lines as Scalar * font_size as Scalar + (num_lines - 1) as Scalar * line_spacing
} else {
0.0
}
}
pub fn lines<I>(text: &str, ranges: I) -> Lines<I>
where I: Iterator<Item=std::ops::Range<usize>>,
{
Lines {
text: text,
ranges: ranges,
}
}
pub fn f32_pt_to_px(font_size_in_points: f32) -> f32 {
font_size_in_points * 4.0 / 3.0
}
pub fn f32_pt_to_scale(font_size_in_points: f32) -> Scale {
Scale::uniform(f32_pt_to_px(font_size_in_points))
}
pub fn pt_to_px(font_size_in_points: FontSize) -> f32 {
f32_pt_to_px(font_size_in_points as f32)
}
pub fn pt_to_scale(font_size_in_points: FontSize) -> Scale {
Scale::uniform(pt_to_px(font_size_in_points))
}
impl<'a, I> Iterator for Lines<'a, I>
where I: Iterator<Item=std::ops::Range<usize>>,
{
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
let Lines { text, ref mut ranges } = *self;
ranges.next().map(|range| &text[range])
}
}
pub mod font {
use fnv;
use std;
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct Id(usize);
#[derive(Debug)]
pub struct Map {
next_index: usize,
map: fnv::FnvHashMap<Id, super::Font>,
}
pub struct NewIds {
index_range: std::ops::Range<usize>,
}
#[derive(Clone)]
pub struct Ids<'a> {
keys: std::collections::hash_map::Keys<'a, Id, super::Font>,
}
#[derive(Debug)]
pub enum Error {
IO(std::io::Error),
NoFont,
}
impl Id {
pub fn index(self) -> usize {
self.0
}
}
impl Map {
pub fn new() -> Self {
Map {
next_index: 0,
map: fnv::FnvHashMap::default(),
}
}
pub fn get(&self, id: Id) -> Option<&super::Font> {
self.map.get(&id)
}
pub fn insert(&mut self, font: super::Font) -> Id {
let index = self.next_index;
self.next_index = index.wrapping_add(1);
let id = Id(index);
self.map.insert(id, font);
id
}
pub fn insert_from_file<P>(&mut self, path: P) -> Result<Id, Error>
where P: AsRef<std::path::Path>,
{
let font = from_file(path)?;
Ok(self.insert(font))
}
pub fn ids(&self) -> Ids {
Ids { keys: self.map.keys() }
}
}
pub fn collection_from_file<P>(path: P) -> Result<super::FontCollection, std::io::Error>
where P: AsRef<std::path::Path>,
{
use std::io::Read;
let path = path.as_ref();
let mut file = std::fs::File::open(path)?;
let mut file_buffer = Vec::new();
file.read_to_end(&mut file_buffer)?;
Ok(super::FontCollection::from_bytes(file_buffer)?)
}
pub fn from_file<P>(path: P) -> Result<super::Font, Error>
where P: AsRef<std::path::Path>
{
let collection = collection_from_file(path)?;
collection.into_font().or(Err(Error::NoFont))
}
impl Iterator for NewIds {
type Item = Id;
fn next(&mut self) -> Option<Self::Item> {
self.index_range.next().map(|i| Id(i))
}
}
impl<'a> Iterator for Ids<'a> {
type Item = Id;
fn next(&mut self) -> Option<Self::Item> {
self.keys.next().map(|&id| id)
}
}
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
Error::IO(e)
}
}
impl std::error::Error for Error {
fn cause(&self) -> Option<&dyn std::error::Error> {
match *self {
Error::IO(ref e) => Some(e),
_ => None,
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
let s = match *self {
Error::IO(ref e) => return std::fmt::Display::fmt(e, f),
Error::NoFont => "No `Font` found in the loaded `FontCollection`.",
};
write!(f, "{}", s)
}
}
}
pub mod glyph {
use {FontSize, Range, Rect, Scalar};
use std;
pub type X = Scalar;
pub type HalfW = Scalar;
pub struct Rects<'a, 'b> {
y: Range,
next_left: Scalar,
layout: super::LayoutIter<'a, 'b>,
}
pub struct RectsPerLine<'a, I> {
lines_with_rects: I,
font: &'a super::Font,
font_size: FontSize,
}
pub struct SelectedRectsPerLine<'a, I> {
enumerated_rects_per_line: std::iter::Enumerate<RectsPerLine<'a, I>>,
start_cursor_idx: super::cursor::Index,
end_cursor_idx: super::cursor::Index,
}
pub struct SelectedRects<'a, 'b> {
enumerated_rects: std::iter::Enumerate<Rects<'a, 'b>>,
end_char_idx: usize,
}
pub fn index_after_cursor<I>(mut line_infos: I,
cursor_idx: super::cursor::Index) -> Option<usize>
where I: Iterator<Item=super::line::Info>,
{
line_infos
.nth(cursor_idx.line)
.and_then(|line_info| {
let start_char = line_info.start_char;
let end_char = line_info.end_char();
let char_index = start_char + cursor_idx.char;
if char_index <= end_char { Some(char_index) } else { None }
})
}
pub fn rects_per_line<'a, I>(lines_with_rects: I,
font: &'a super::Font,
font_size: FontSize) -> RectsPerLine<'a, I>
where I: Iterator<Item=(&'a str, Rect)>,
{
RectsPerLine {
lines_with_rects: lines_with_rects,
font: font,
font_size: font_size,
}
}
pub fn selected_rects_per_line<'a, I>(lines_with_rects: I,
font: &'a super::Font,
font_size: FontSize,
start: super::cursor::Index,
end: super::cursor::Index) -> SelectedRectsPerLine<'a, I>
where I: Iterator<Item=(&'a str, Rect)>,
{
SelectedRectsPerLine {
enumerated_rects_per_line:
rects_per_line(lines_with_rects, font, font_size).enumerate(),
start_cursor_idx: start,
end_cursor_idx: end,
}
}
impl<'a, I> Iterator for RectsPerLine<'a, I>
where I: Iterator<Item=(&'a str, Rect)>,
{
type Item = Rects<'a, 'a>;
fn next(&mut self) -> Option<Self::Item> {
let RectsPerLine { ref mut lines_with_rects, font, font_size } = *self;
let scale = super::pt_to_scale(font_size);
lines_with_rects.next().map(|(line, line_rect)| {
let (x, y) = (line_rect.left() as f32, line_rect.top() as f32);
let point = super::rt::Point { x: x, y: y };
Rects {
next_left: line_rect.x.start,
layout: font.layout(line, scale, point),
y: line_rect.y
}
})
}
}
impl<'a, I> Iterator for SelectedRectsPerLine<'a, I>
where I: Iterator<Item=(&'a str, Rect)>,
{
type Item = SelectedRects<'a, 'a>;
fn next(&mut self) -> Option<Self::Item> {
let SelectedRectsPerLine {
ref mut enumerated_rects_per_line,
start_cursor_idx,
end_cursor_idx,
} = *self;
enumerated_rects_per_line.next().map(|(i, rects)| {
let end_char_idx =
if i == end_cursor_idx.line {
end_cursor_idx.char
} else if start_cursor_idx.line <= i && i < end_cursor_idx.line {
std::u32::MAX as usize
} else {
0
};
let mut enumerated_rects = rects.enumerate();
if i == start_cursor_idx.line {
for _ in 0..start_cursor_idx.char {
enumerated_rects.next();
}
}
SelectedRects {
enumerated_rects: enumerated_rects,
end_char_idx: end_char_idx,
}
})
}
}
impl<'a, 'b> Iterator for Rects<'a, 'b> {
type Item = Rect;
fn next(&mut self) -> Option<Self::Item> {
let Rects { ref mut next_left, ref mut layout, y } = *self;
layout.next().map(|g| {
let left = *next_left;
let right = g.pixel_bounding_box()
.map(|bb| bb.max.x as Scalar)
.unwrap_or_else(|| left + g.unpositioned().h_metrics().advance_width as Scalar);
*next_left = right;
let x = Range::new(left, right);
Rect { x: x, y: y }
})
}
}
impl<'a, 'b> Iterator for SelectedRects<'a, 'b> {
type Item = Rect;
fn next(&mut self) -> Option<Self::Item> {
let SelectedRects { ref mut enumerated_rects, end_char_idx } = *self;
enumerated_rects.next()
.and_then(|(i, rect)| {
if i < end_char_idx { Some(rect) }
else { None }
})
}
}
}
pub mod cursor {
use FontSize;
use position::{Range, Rect, Scalar, Point, Align};
use std;
#[derive(Clone)]
pub struct XysPerLine<'a, I> {
lines_with_rects: I,
font: &'a super::Font,
text: &'a str,
font_size: FontSize,
}
#[derive(Clone)]
pub struct XysPerLineFromText<'a> {
xys_per_line: XysPerLine<'a,
std::iter::Zip<std::iter::Cloned<std::slice::Iter<'a, super::line::Info>>,
super::line::Rects<std::iter::Cloned<std::slice::Iter<'a, super::line::Info>>>>
>,
}
pub struct Xs<'a, 'b> {
next_x: Option<Scalar>,
layout: super::LayoutIter<'a, 'b>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Index {
pub line: usize,
pub char: usize,
}
impl Index {
pub fn previous_word_start<I>(self, text: &str, mut line_infos: I) -> Option<Self>
where I: Iterator<Item=super::line::Info>,
{
let Index { line, char } = self;
if char > 0 {
line_infos.nth(line).and_then(|line_info| {
let line_count = line_info.char_range().count();
let mut chars_rev = (&text[line_info.byte_range()]).chars().rev();
if char != line_count {
chars_rev.nth(line_count - char - 1);
}
let mut new_char = 0;
let mut hit_non_whitespace = false;
for (i, char_) in chars_rev.enumerate() {
if !char_.is_whitespace() { hit_non_whitespace = true; }
if char_.is_whitespace() && hit_non_whitespace {
new_char = char - i;
break
}
}
Some(Index { line: line, char: new_char })
})
} else {
self.previous(line_infos)
}
}
pub fn next_word_end<I>(self, text: &str, mut line_infos: I) -> Option<Self>
where I: Iterator<Item=super::line::Info>,
{
let Index { line, char } = self;
line_infos.nth(line)
.and_then(|line_info| {
let line_count = line_info.char_range().count();
if char < line_count {
let mut chars = (&text[line_info.byte_range()]).chars();
let mut new_char = line_count;
let mut hit_non_whitespace = false;
if char != 0 {
chars.nth(char - 1);
}
for (i, char_) in chars.enumerate() {
if !char_.is_whitespace() { hit_non_whitespace = true; }
if char_.is_whitespace() && hit_non_whitespace {
new_char = char + i;
break
}
}
Some(Index { line: line, char: new_char })
} else {
line_infos.next().map(|_| Index { line: line + 1, char: 0 })
}
})
}
pub fn previous<I>(self, mut line_infos: I) -> Option<Self>
where I: Iterator<Item=super::line::Info>,
{
let Index { line, char } = self;
if char > 0 {
let new_char = char - 1;
line_infos.nth(line)
.and_then(|info| if new_char <= info.char_range().count() {
Some(Index { line: line, char: new_char })
} else {
None
})
} else if line > 0 {
let new_line = line - 1;
line_infos.nth(new_line)
.map(|info| {
let new_char = info.end_char() - info.start_char;
Index { line: new_line, char: new_char }
})
} else {
None
}
}
pub fn next<I>(self, mut line_infos: I) -> Option<Self>
where I: Iterator<Item=super::line::Info>,
{
let Index { line, char } = self;
line_infos.nth(line)
.and_then(|info| {
if char >= info.char_range().count() {
line_infos.next().map(|_| Index { line: line + 1, char: 0 })
} else {
Some(Index { line: line, char: char + 1 })
}
})
}
pub fn clamp_to_lines<I>(self, line_infos: I) -> Self
where I: Iterator<Item=super::line::Info>,
{
let mut last = None;
for (i, info) in line_infos.enumerate() {
if i == self.line {
let num_chars = info.char_range().len();
let char = std::cmp::min(self.char, num_chars);
return Index { line: i, char: char };
}
last = Some((i, info));
}
match last {
Some((i, info)) => Index {
line: i,
char: info.char_range().len(),
},
None => Index { line: 0, char: 0 },
}
}
}
pub fn xys_per_line<'a, I>(lines_with_rects: I,
font: &'a super::Font,
text: &'a str,
font_size: FontSize) -> XysPerLine<'a, I>
{
XysPerLine {
lines_with_rects: lines_with_rects,
font: font,
text: text,
font_size: font_size,
}
}
pub fn xys_per_line_from_text<'a>(text: &'a str,
line_infos: &'a [super::line::Info],
font: &'a super::Font,
font_size: FontSize,
x_align: super::Justify,
y_align: Align,
line_spacing: Scalar,
rect: Rect) -> XysPerLineFromText<'a>
{
let line_infos = line_infos.iter().cloned();
let line_rects = super::line::rects(line_infos.clone(), font_size, rect,
x_align, y_align, line_spacing);
let lines = line_infos.clone();
let lines_with_rects = lines.zip(line_rects.clone());
XysPerLineFromText {
xys_per_line: super::cursor::xys_per_line(lines_with_rects, font, text, font_size),
}
}
pub fn index_before_char<I>(line_infos: I, char_index: usize) -> Option<Index>
where I: Iterator<Item=super::line::Info>,
{
for (i, line_info) in line_infos.enumerate() {
let start_char = line_info.start_char;
let end_char = line_info.end_char();
if start_char <= char_index && char_index <= end_char {
return Some(Index { line: i, char: char_index - start_char });
}
}
None
}
pub fn xy_at<'a, I>(xys_per_line: I, idx: Index) -> Option<(Scalar, Range)>
where I: Iterator<Item=(Xs<'a, 'a>, Range)>,
{
for (i, (xs, y)) in xys_per_line.enumerate() {
if i == idx.line {
for (j, x) in xs.enumerate() {
if j == idx.char {
return Some((x, y));
}
}
}
}
None
}
pub fn closest_line<'a, I>(y_pos: Scalar, xys_per_line: I) -> Option<(usize, Xs<'a,'a>, Range)>
where I: Iterator<Item = (Xs<'a, 'a>, Range)>,
{
let mut xys_per_line_enumerated = xys_per_line.enumerate();
xys_per_line_enumerated.next().and_then(|(first_line_idx, (first_line_xs, first_line_y))| {
let mut closest_line = (first_line_idx,first_line_xs,first_line_y);
let mut closest_diff = (y_pos - first_line_y.middle()).abs();
for (line_idx, (line_xs, line_y)) in xys_per_line_enumerated {
if line_y.is_over(y_pos) {
closest_line = (line_idx,line_xs,line_y);
break;
} else {
let diff = (y_pos - line_y.middle()).abs();
if diff < closest_diff {
closest_line = (line_idx,line_xs,line_y);
closest_diff = diff;
} else {
break;
}
}
}
Some(closest_line)
})
}
pub fn closest_cursor_index_and_xy<'a, I>(xy: Point, xys_per_line: I) -> Option<(Index, Point)>
where I: Iterator<Item = (Xs<'a, 'a>, Range)>,
{
closest_line(xy[1], xys_per_line)
.and_then(|(closest_line_idx, closest_line_xs, closest_line_y)| {
let (closest_char_idx, closest_x) =
closest_cursor_index_on_line(xy[0], closest_line_xs);
let index = Index {
line: closest_line_idx,
char: closest_char_idx,
};
let point = [closest_x, closest_line_y.middle()];
Some((index, point))
})
}
pub fn closest_cursor_index_on_line<'a>(x_pos: Scalar, line_xs: Xs<'a, 'a>) -> (usize, Scalar) {
let mut xs_enumerated = line_xs.enumerate();
let (first_idx, first_x) = xs_enumerated.next().unwrap();
let first_diff = (x_pos - first_x).abs();
let mut closest = (first_idx,first_x);
let mut closest_diff = first_diff;
for (i, x) in xs_enumerated {
let diff = (x_pos - x).abs();
if diff < closest_diff {
closest = (i,x);
closest_diff = diff;
} else {
break;
}
}
closest
}
impl<'a, I> Iterator for XysPerLine<'a, I>
where I: Iterator<Item=(super::line::Info, Rect)>,
{
type Item = (Xs<'a, 'a>, Range);
fn next(&mut self) -> Option<Self::Item> {
let XysPerLine { ref mut lines_with_rects, font, text, font_size } = *self;
let scale = super::pt_to_scale(font_size);
lines_with_rects.next().map(|(line_info, line_rect)| {
let line = &text[line_info.byte_range()];
let (x, y) = (line_rect.left() as f32, line_rect.top() as f32);
let point = super::rt::Point { x: x, y: y };
let y = line_rect.y;
let layout = font.layout(line, scale, point);
let xs = Xs {
next_x: Some(line_rect.x.start),
layout: layout,
};
(xs, y)
})
}
}
impl<'a> Iterator for XysPerLineFromText<'a> {
type Item = (Xs<'a, 'a>, Range);
fn next(&mut self) -> Option<Self::Item> {
self.xys_per_line.next()
}
}
impl<'a, 'b> Iterator for Xs<'a, 'b> {
type Item = Scalar;
fn next(&mut self) -> Option<Self::Item> {
self.next_x.map(|x| {
self.next_x = self.layout.next()
.map(|g| {
g.pixel_bounding_box()
.map(|r| r.max.x as Scalar)
.unwrap_or_else(|| {
x + g.unpositioned().h_metrics().advance_width as Scalar
})
});
x
})
}
}
}
pub mod line {
use FontSize;
use position::{Align, Range, Rect, Scalar};
use std;
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Break {
Wrap {
byte: usize,
char: usize,
len_bytes: usize,
},
Newline {
byte: usize,
char: usize,
len_bytes: usize,
},
End {
byte: usize,
char: usize,
},
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Info {
pub start_byte: usize,
pub start_char: usize,
pub end_break: Break,
pub width: Scalar,
}
pub struct Infos<'a, F> {
text: &'a str,
font: &'a super::Font,
font_size: FontSize,
max_width: Scalar,
next_break_fn: F,
start_byte: usize,
start_char: usize,
last_break: Option<Break>,
}
#[derive(Clone)]
pub struct Rects<I> {
infos: I,
x_align: super::Justify,
line_spacing: Scalar,
next: Option<Rect>,
}
pub struct SelectedRects<'a, I> {
selected_char_rects_per_line: super::glyph::SelectedRectsPerLine<'a, I>,
}
pub type NextBreakFnPtr = fn(&str, &super::Font, FontSize, Scalar) -> (Break, Scalar);
impl Break {
pub fn byte_index(self) -> usize {
match self {
Break::Wrap { byte, .. } |
Break::Newline { byte, .. } |
Break::End { byte, .. } => byte,
}
}
pub fn char_index(self) -> usize {
match self {
Break::Wrap { char, .. } |
Break::Newline { char, .. } |
Break::End { char, .. } => char,
}
}
}
impl<'a, F> Clone for Infos<'a, F>
where F: Clone,
{
fn clone(&self) -> Self {
Infos {
text: self.text,
font: self.font,
font_size: self.font_size,
max_width: self.max_width,
next_break_fn: self.next_break_fn.clone(),
start_byte: self.start_byte,
start_char: self.start_char,
last_break: None,
}
}
}
impl Info {
pub fn end_byte(&self) -> usize {
self.end_break.byte_index()
}
pub fn end_char(&self) -> usize {
self.end_break.char_index()
}
pub fn byte_range(self) -> std::ops::Range<usize> {
self.start_byte..self.end_byte()
}
pub fn char_range(self) -> std::ops::Range<usize> {
self.start_char..self.end_char()
}
}
impl<'a> Infos<'a, NextBreakFnPtr> {
pub fn wrap_by_character(mut self, max_width: Scalar) -> Self {
self.next_break_fn = next_break_by_character;
self.max_width = max_width;
self
}
pub fn wrap_by_whitespace(mut self, max_width: Scalar) -> Self {
self.next_break_fn = next_break_by_whitespace;
self.max_width = max_width;
self
}
}
fn advance_width(ch: char,
font: &super::Font,
scale: super::Scale,
last_glyph: &mut Option<super::GlyphId>) -> Scalar
{
let g = font.glyph(ch).scaled(scale);
let kern = last_glyph
.map(|last| font.pair_kerning(scale, last, g.id()))
.unwrap_or(0.0);
let advance_width = g.h_metrics().advance_width;
*last_glyph = Some(g.id());
(kern + advance_width) as Scalar
}
fn next_break(text: &str,
font: &super::Font,
font_size: FontSize) -> (Break, Scalar)
{
let scale = super::pt_to_scale(font_size);
let mut width = 0.0;
let mut char_i = 0;
let mut char_indices = text.char_indices().peekable();
let mut last_glyph = None;
while let Some((byte_i, ch)) = char_indices.next() {
if ch == '\r' {
if let Some(&(_, '\n')) = char_indices.peek() {
let break_ = Break::Newline { byte: byte_i, char: char_i, len_bytes: 2 };
return (break_, width);
}
} else if ch == '\n' {
let break_ = Break::Newline { byte: byte_i, char: char_i, len_bytes: 1 };
return (break_, width);
}
width += advance_width(ch, font, scale, &mut last_glyph);
char_i += 1;
}
let break_ = Break::End { byte: text.len(), char: char_i };
(break_, width)
}
fn next_break_by_character(text: &str,
font: &super::Font,
font_size: FontSize,
max_width: Scalar) -> (Break, Scalar)
{
let scale = super::pt_to_scale(font_size);
let mut width = 0.0;
let mut char_i = 0;
let mut char_indices = text.char_indices().peekable();
let mut last_glyph = None;
while let Some((byte_i, ch)) = char_indices.next() {
if ch == '\r' {
if let Some(&(_, '\n')) = char_indices.peek() {
let break_ = Break::Newline { byte: byte_i, char: char_i, len_bytes: 2 };
return (break_, width);
}
} else if ch == '\n' {
let break_ = Break::Newline { byte: byte_i, char: char_i, len_bytes: 1 };
return (break_, width);
}
let new_width = width + advance_width(ch, font, scale, &mut last_glyph);
if new_width > max_width {
let break_ = Break::Wrap { byte: byte_i, char: char_i, len_bytes: 0 };
return (break_, width);
}
width = new_width;
char_i += 1;
}
let break_ = Break::End { byte: text.len(), char: char_i };
(break_, width)
}
fn next_break_by_whitespace(text: &str,
font: &super::Font,
font_size: FontSize,
max_width: Scalar) -> (Break, Scalar)
{
struct Last { byte: usize, char: usize, width_before: Scalar }
let scale = super::pt_to_scale(font_size);
let mut last_whitespace_start = None;
let mut width = 0.0;
let mut char_i = 0;
let mut char_indices = text.char_indices().peekable();
let mut last_glyph = None;
while let Some((byte_i, ch)) = char_indices.next() {
if ch == '\r' {
if let Some(&(_, '\n')) = char_indices.peek() {
let break_ = Break::Newline { byte: byte_i, char: char_i, len_bytes: 2 };
return (break_, width)
}
} else if ch == '\n' {
let break_ = Break::Newline { byte: byte_i, char: char_i, len_bytes: 1 };
return (break_, width);
}
let new_width = width + advance_width(ch, font, scale, &mut last_glyph);
if width > max_width {
match last_whitespace_start {
Some((Last { byte, char, width_before }, len_bytes)) => {
let break_ = Break::Wrap { byte: byte, char: char, len_bytes };
return (break_, width_before);
},
None => {
let break_ = Break::Wrap { byte: byte_i, char: char_i, len_bytes: 0 };
return (break_, width);
}
}
}
if ch.is_whitespace() {
last_whitespace_start = Some((Last { byte: byte_i, char: char_i, width_before: width }, ch.len_utf8()));
}
width = new_width;
char_i += 1;
}
let break_ = Break::End { byte: text.len(), char: char_i };
(break_, width)
}
pub fn width(text: &str, font: &super::Font, font_size: FontSize) -> Scalar {
let scale = super::Scale::uniform(super::pt_to_px(font_size));
let point = super::rt::Point { x: 0.0, y: 0.0 };
let mut total_w = 0.0;
for g in font.layout(text, scale, point) {
match g.pixel_bounding_box() {
Some(bb) => total_w = bb.max.x as f32,
None => total_w += g.unpositioned().h_metrics().advance_width,
}
}
total_w as Scalar
}
pub fn infos_wrapped_by<'a, F>(text: &'a str,
font: &'a super::Font,
font_size: FontSize,
max_width: Scalar,
next_break_fn: F) -> Infos<'a, F>
where F: for<'b> FnMut(&'b str, &'b super::Font, FontSize, Scalar) -> (Break, Scalar)
{
Infos {
text: text,
font: font,
font_size: font_size,
max_width: max_width,
next_break_fn: next_break_fn,
start_byte: 0,
start_char: 0,
last_break: None,
}
}
pub fn infos<'a>(text: &'a str,
font: &'a super::Font,
font_size: FontSize) -> Infos<'a, NextBreakFnPtr>
{
fn no_wrap(text: &str,
font: &super::Font,
font_size: FontSize,
_max_width: Scalar) -> (Break, Scalar)
{
next_break(text, font, font_size)
}
infos_wrapped_by(text, font, font_size, std::f64::MAX, no_wrap)
}
pub fn rects<I>(mut infos: I,
font_size: FontSize,
bounding_rect: Rect,
x_align: super::Justify,
y_align: Align,
line_spacing: Scalar) -> Rects<I>
where I: Iterator<Item=Info> + ExactSizeIterator,
{
let num_lines = infos.len();
let first_rect = infos.next().map(|first_info| {
let range = Range::new(0.0, first_info.width);
let x = match x_align {
super::Justify::Left => range.align_start_of(bounding_rect.x),
super::Justify::Center => range.align_middle_of(bounding_rect.x),
super::Justify::Right => range.align_end_of(bounding_rect.x),
};
let total_text_height = super::height(num_lines, font_size, line_spacing);
let total_text_y_range = Range::new(0.0, total_text_height);
let total_text_y = match y_align {
Align::Start => total_text_y_range.align_start_of(bounding_rect.y),
Align::Middle => total_text_y_range.align_middle_of(bounding_rect.y),
Align::End => total_text_y_range.align_end_of(bounding_rect.y),
};
let range = Range::new(0.0, font_size as Scalar);
let y = range.align_end_of(total_text_y);
Rect { x: x, y: y }
});
Rects {
infos: infos,
next: first_rect,
x_align: x_align,
line_spacing: line_spacing,
}
}
pub fn selected_rects<'a, I>(lines_with_rects: I,
font: &'a super::Font,
font_size: FontSize,
start: super::cursor::Index,
end: super::cursor::Index) -> SelectedRects<'a, I>
where I: Iterator<Item=(&'a str, Rect)>,
{
SelectedRects {
selected_char_rects_per_line:
super::glyph::selected_rects_per_line(lines_with_rects, font, font_size, start, end)
}
}
impl<'a, F> Iterator for Infos<'a, F>
where F: for<'b> FnMut(&'b str, &'b super::Font, FontSize, Scalar) -> (Break, Scalar)
{
type Item = Info;
fn next(&mut self) -> Option<Self::Item> {
let Infos {
text,
font,
font_size,
max_width,
ref mut next_break_fn,
ref mut start_byte,
ref mut start_char,
ref mut last_break,
} = *self;
match next_break_fn(&text[*start_byte..], font, font_size, max_width) {
(next @ Break::Newline { .. }, width) | (next @ Break::Wrap { .. }, width) => {
let next_break = match next {
Break::Newline { byte, char, len_bytes } =>
Break::Newline {
byte: *start_byte + byte,
char: *start_char + char,
len_bytes: len_bytes,
},
Break::Wrap { byte, char, len_bytes } =>
Break::Wrap {
byte: *start_byte + byte,
char: *start_char + char,
len_bytes: len_bytes,
},
_ => unreachable!(),
};
let info = Info {
start_byte: *start_byte,
start_char: *start_char,
end_break: next_break,
width: width,
};
match next {
Break::Newline { byte, char, len_bytes } |
Break::Wrap { byte, char, len_bytes } => {
*start_byte = info.start_byte + byte + len_bytes;
*start_char = info.start_char + char + 1;
},
_ => unreachable!(),
};
*last_break = Some(next_break);
Some(info)
},
(Break::End { char, .. }, width) => {
let empty_line = {
match *last_break {
Some(last_break_) => match last_break_ {
Break::Newline { .. } => true,
_ => false,
}, None => true,
}
};
if *start_byte < text.len() || empty_line {
let total_bytes = text.len();
let total_chars = *start_char + char;
let end_break = Break::End {
byte: total_bytes,
char: total_chars,
};
let info = Info {
start_byte: *start_byte,
start_char: *start_char,
end_break: end_break,
width: width,
};
*start_byte = total_bytes;
*start_char = total_chars;
*last_break = Some(end_break);
Some(info)
} else {
None
}
},
}
}
}
impl<I> Iterator for Rects<I>
where I: Iterator<Item=Info>,
{
type Item = Rect;
fn next(&mut self) -> Option<Self::Item> {
let Rects { ref mut next, ref mut infos, x_align, line_spacing } = *self;
next.map(|line_rect| {
*next = infos.next().map(|info| {
let y = {
let h = line_rect.h();
let y = line_rect.y() - h - line_spacing;
Range::from_pos_and_len(y, h)
};
let x = {
let range = Range::new(0.0, info.width);
match x_align {
super::Justify::Left => range.align_start_of(line_rect.x),
super::Justify::Center => range.align_middle_of(line_rect.x),
super::Justify::Right => range.align_end_of(line_rect.x),
}
};
Rect { x: x, y: y }
});
line_rect
})
}
}
impl<'a, I> Iterator for SelectedRects<'a, I>
where I: Iterator<Item=(&'a str, Rect)>,
{
type Item = Rect;
fn next(&mut self) -> Option<Self::Item> {
while let Some(mut rects) = self.selected_char_rects_per_line.next() {
if let Some(first_rect) = rects.next() {
let total_selected_rect = rects.fold(first_rect, |mut total, next| {
total.x.end = next.x.end;
total
});
return Some(total_selected_rect);
}
}
None
}
}
}