use std::cell::OnceCell;
use std::cmp::min;
use std::fmt;
use std::fmt::{Display, Formatter, Write};
use std::slice;
use std::sync::Arc;
use parking_lot::RwLock;
use crate::NonExhaustive;
use crate::render::font::size::{FontSize, FontSizer, ScaledFontSize};
use crate::render::RenderTarget;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LayoutHorizontalAlignment {
Left,
Center,
Right,
}
impl Default for LayoutHorizontalAlignment {
#[inline]
fn default() -> Self { LayoutHorizontalAlignment::Left }
}
impl LayoutHorizontalAlignment {
pub fn offset(&self, inner: f32, outer: f32) -> f32 {
match self {
LayoutHorizontalAlignment::Left => 0.0,
LayoutHorizontalAlignment::Center => (outer - inner) / 2.0,
LayoutHorizontalAlignment::Right => outer - inner,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LayoutVerticalAlignment {
Top,
Center,
Bottom,
}
impl Default for LayoutVerticalAlignment {
#[inline]
fn default() -> Self { LayoutVerticalAlignment::Top }
}
impl LayoutVerticalAlignment {
pub fn offset(&self, inner: f32, outer: f32) -> f32 {
match self {
LayoutVerticalAlignment::Top => 0.0,
LayoutVerticalAlignment::Center => (outer - inner) / 2.0,
LayoutVerticalAlignment::Bottom => outer - inner,
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct LayoutAlignment {
pub horizontal: LayoutHorizontalAlignment,
pub vertical: LayoutVerticalAlignment,
}
impl LayoutAlignment {
#[inline]
pub fn offset(&self, inner: &glm::TVec2<f32>, outer: &glm::TVec2<f32>) -> glm::TVec2<f32> {
glm::vec2(
self.horizontal.offset(inner.x, outer.x),
self.vertical.offset(inner.y, outer.y),
)
}
}
#[derive(Debug, Clone)]
pub struct PositionedChar {
pub value: char,
pub position: glm::TVec2<f32>,
pub size: ScaledFontSize,
pub color: glm::TVec3<f32>,
}
#[derive(Debug, Clone, Copy)]
pub struct TextLayoutCanvas {
pub offset: glm::TVec2<f32>,
pub size: glm::TVec2<f32>,
}
#[derive(Debug)]
pub struct TextLayoutInfo {
sizer: Arc<dyn FontSizer>,
canvas: RwLock<TextLayoutCanvas>,
max_line_count: Option<usize>,
size: ScaledFontSize,
alignment: LayoutAlignment,
word_wrap: bool,
}
impl TextLayoutInfo {
#[inline]
pub fn canvas(&self) -> TextLayoutCanvas { self.canvas.read().clone() }
#[inline]
pub fn max_line_count(&self) -> Option<usize> { self.max_line_count }
#[inline]
pub fn size(&self) -> ScaledFontSize { self.size }
#[inline]
pub fn alignment(&self) -> LayoutAlignment { self.alignment }
#[inline]
pub fn word_wrap(&self) -> bool { self.word_wrap }
}
#[derive(Debug, Clone)]
pub struct TextLayoutCreateInfo {
pub canvas_offset: glm::TVec2<f32>,
pub canvas_size: Option<glm::TVec2<f32>>,
pub max_line_count: Option<usize>,
pub size: FontSize,
pub alignment: LayoutAlignment,
pub word_wrap: bool,
pub _ne: NonExhaustive,
}
impl Default for TextLayoutCreateInfo {
fn default() -> Self {
Self {
canvas_offset: glm::vec2(0.0, 0.0),
canvas_size: None,
max_line_count: None,
size: FontSize::default(),
alignment: LayoutAlignment::default(),
word_wrap: false,
_ne: NonExhaustive(()),
}
}
}
impl TextLayoutCreateInfo {
fn into_info(self, sizer: Arc<dyn FontSizer>, scale: f32) -> TextLayoutInfo {
TextLayoutInfo {
sizer,
canvas: RwLock::new(TextLayoutCanvas {
offset: self.canvas_offset,
size: self.canvas_size
.expect("TextLayoutCreateInfo::canvas_size is required to be Some()"),
}),
max_line_count: self.max_line_count,
size: self.size.scale(scale),
alignment: self.alignment,
word_wrap: self.word_wrap,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct FormattedChar {
pub value: char,
width: f32,
pub color: glm::TVec3<f32>
}
impl FormattedChar {
fn new(info: &TextLayoutInfo, value: char, color: glm::TVec3<f32>) -> Self {
FormattedChar {
value,
width: info.sizer.h_advance(info.size, value),
color,
}
}
#[inline]
pub fn value(&self) -> char { self.value }
#[inline]
pub fn width(&self) -> f32 { self.width }
#[inline]
pub fn color(&self) -> glm::TVec3<f32> { self.color }
}
#[derive(Debug, Clone, Copy)]
pub struct LineChar {
inner: FormattedChar,
x: f32,
line_no: usize,
}
impl LineChar {
fn new(info: &TextLayoutInfo, value: char, color: glm::TVec3<f32>) -> Self {
LineChar {
inner: FormattedChar::new(info, value, color),
x: 0.0,
line_no: 0,
}
}
#[inline]
pub fn value(&self) -> &FormattedChar { &self.inner }
#[inline]
pub fn x(&self) -> f32 { self.x }
#[inline]
pub fn line_no(&self) -> usize { self.line_no }
}
impl From<FormattedChar> for LineChar {
fn from(value: FormattedChar) -> Self {
Self {
inner: value,
x: 0.0,
line_no: 0,
}
}
}
impl From<&LineChar> for LineChar {
fn from(value: &LineChar) -> Self { value.clone() }
}
#[derive(Debug, Clone, Copy)]
struct LineSegment {
start: usize,
end: usize,
}
impl LineSegment {
fn new(start: usize, end: usize) -> Self {
assert!(start <= end);
Self {
start,
end,
}
}
}
pub struct PhysicalLineIter<'a> {
chars: &'a [LineChar],
idx: usize,
r_idx: usize,
}
impl<'a> PhysicalLineIter<'a> {
fn new(chars: &'a [LineChar]) -> Self {
Self {
chars,
idx: 0,
r_idx: chars.len(),
}
}
pub fn width(&self) -> f32 {
match self.chars.last() {
Some(last) => last.x + last.inner.width,
None => 0.0,
}
}
}
impl<'a> Iterator for PhysicalLineIter<'a> {
type Item = &'a LineChar;
fn next(&mut self) -> Option<Self::Item> {
if self.len() > 0 {
let r = self.chars.get(self.idx);
self.idx += 1;
r
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let size = self.r_idx - self.idx;
(size, Some(size))
}
}
impl ExactSizeIterator for PhysicalLineIter<'_> {}
impl DoubleEndedIterator for PhysicalLineIter<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.len() > 0 {
self.r_idx -= 1;
self.chars.get(self.r_idx)
} else {
None
}
}
}
pub struct LogicalLineIter<'a> {
chars: &'a Vec<LineChar>,
iter: slice::Iter<'a, LineSegment>,
}
impl<'a> LogicalLineIter<'a> {
fn phys_line_iter(&self, segment: &LineSegment) -> PhysicalLineIter<'a> {
PhysicalLineIter::new(&self.chars[segment.start..segment.end])
}
}
impl<'a> Iterator for LogicalLineIter<'a> {
type Item = PhysicalLineIter<'a>;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().map(|segment| self.phys_line_iter(segment))
}
fn size_hint(&self) -> (usize, Option<usize>) { self.iter.size_hint() }
}
impl ExactSizeIterator for LogicalLineIter<'_> {}
impl DoubleEndedIterator for LogicalLineIter<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
self.iter.next_back().map(|segment| self.phys_line_iter(segment))
}
}
#[derive(Debug)]
pub struct LogicalLine {
chars: Vec<LineChar>,
segments: Vec<LineSegment>,
info: Arc<TextLayoutInfo>,
cache_width: OnceCell<f32>,
cache_height: OnceCell<f32>,
}
impl LogicalLine {
fn new(info: Arc<TextLayoutInfo>) -> Self {
Self {
chars: vec![],
segments: vec![LineSegment::new(0, 0)],
info,
cache_width: OnceCell::new(),
cache_height: OnceCell::new(),
}
}
fn for_buffer<T>(info: Arc<TextLayoutInfo>, buffer: T) -> Self
where
T: IntoIterator,
T::Item: Into<LineChar>,
{
let chars = buffer.into_iter()
.map(T::Item::into)
.collect();
let mut line = Self {
chars,
segments: vec![],
info,
cache_width: OnceCell::new(),
cache_height: OnceCell::new(),
};
line.adjust_trailing(0);
line
}
#[allow(unused)]
fn for_string(info: Arc<TextLayoutInfo>, val: &str, color: glm::TVec3<f32>) -> Self {
Self::for_buffer(
info.clone(),
val.chars().map(|c| FormattedChar::new(&info, c, color)),
)
}
#[inline]
pub fn physical_lines(&self) -> LogicalLineIter<'_> {
LogicalLineIter {
chars: &self.chars,
iter: self.segments.iter(),
}
}
#[inline]
pub fn width(&self) -> f32 {
*self.cache_width.get_or_init(|| {
self.physical_lines()
.map(|phys_line| phys_line.width())
.fold(0.0, f32::max)
})
}
#[inline]
pub fn height(&self) -> f32 {
*self.cache_height.get_or_init(|| {
self.info.sizer.v_advance(self.info.size) * (self.physical_lines().len() - 1) as f32
+ self.info.sizer.height(self.info.size)
})
}
#[inline]
pub fn len(&self) -> usize { self.chars.len() }
#[inline]
pub fn line_count(&self) -> usize { self.segments.len() }
fn move_cursor_up(&self, cursor: &mut glm::TVec2<usize>) -> Option<f32> {
let curr = &self.chars[cursor.x];
if curr.line_no > 0 {
cursor.x = self.index_for_x(curr.x, curr.line_no - 1);
None
} else if cursor.y > 0 {
cursor.x = 0;
cursor.y -= 1;
Some(curr.x)
} else {
None
}
}
fn move_cursor_down(&self, cursor: &mut glm::TVec2<usize>, line_max: usize) -> Option<f32> {
let curr = &self.chars[cursor.x];
if curr.line_no < (self.line_count() - 1) {
cursor.x = self.index_for_x(curr.x, curr.line_no + 1);
None
} else if cursor.y < (line_max - 1) {
cursor.x = 0;
cursor.y += 1;
Some(curr.x)
} else {
None
}
}
fn index_for_x(&self, x: f32, line_no: usize) -> usize {
let phys_line = self.physical_lines().nth(line_no).unwrap();
let mut prev: Option<(usize, &LineChar)> = None;
let mut curr: Option<(usize, &LineChar)> = None;
for (idx, layout_char) in phys_line.enumerate() {
if layout_char.x > x {
curr = Some((idx, layout_char));
break;
} else {
prev = Some((idx, layout_char));
}
}
let local_idx = match (prev, curr) {
(None, None) => 0,
(None, Some((idx, curr))) => {
if x < (curr.x - x) {
0
} else {
idx
}
},
(Some((idx, _prev)), None) => idx,
(Some((idx_prev, prev)), Some((idx_curr, curr))) => {
if (x - prev.x) < (curr.x - x) {
idx_prev
} else {
idx_curr
}
},
};
self.segments[line_no].start + local_idx
}
fn adjust_trailing(&mut self, idx: usize) {
let canvas_size = self.info.canvas.read().size;
let (mut prev_char, mut x, mut line_no) = if idx > 0 {
let prev = &self.chars[idx - 1];
(Some(prev.inner.value), prev.x + prev.inner.width, prev.line_no)
} else {
(None, 0.0, 0)
};
let mut line_start = match self.segments.get(line_no) {
Some(line) => line.start,
None => 0,
};
self.segments.truncate(line_no);
for (local_idx, line_char) in self.chars[idx..].iter_mut().enumerate() {
let curr_idx = idx + local_idx;
let mut kern = match prev_char {
Some(prev_char) => self.info.sizer.kern(self.info.size, prev_char, line_char.inner.value),
None => 0.0,
};
let mut new_x = x + kern + line_char.inner.width;
if self.info.word_wrap && new_x > canvas_size.x && x > 0.0 {
line_no += 1;
self.segments.push(LineSegment::new(line_start, curr_idx));
line_start = curr_idx;
x = 0.0;
kern = 0.0;
new_x = line_char.inner.width;
prev_char = None;
} else {
prev_char = Some(line_char.inner.value);
}
line_char.x = x + kern;
line_char.line_no = line_no;
x = new_x;
}
self.segments.push(LineSegment::new(line_start, self.chars.len()));
self.invalidate_size_caches();
}
fn invalidate_size_caches(&mut self) {
self.cache_width.take();
self.cache_height.take();
}
fn insert(&mut self, idx: usize, val: &str, color: glm::TVec3<f32>) {
for (i_c, c) in val.chars().enumerate() {
self.chars.insert(idx + i_c, LineChar::new(&self.info, c, color));
}
self.adjust_trailing(idx);
}
fn insert_buffer<T>(&mut self, idx: usize, val: T) -> usize
where
T: IntoIterator,
T::Item: Into<LineChar>
{
let mut count_c = 0;
for (i_c, c) in val.into_iter().enumerate() {
self.chars.insert(idx + i_c, c.into());
count_c += 1;
}
self.adjust_trailing(idx);
count_c
}
fn delete(&mut self, idx: usize) {
self.chars.remove(idx);
self.adjust_trailing(idx);
}
fn split_off(&mut self, info: Arc<TextLayoutInfo>, idx: usize) -> Self {
let remainder = Self::for_buffer(info, self.chars.split_off(idx));
self.adjust_trailing(idx);
remainder
}
#[inline]
pub fn iter_build(&self, offset: glm::TVec2<f32>) -> LayoutLineBuildIter<'_> {
LayoutLineBuildIter::new(self, offset)
}
}
impl Display for LogicalLine {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
for c in &self.chars {
f.write_char(c.inner.value)?
}
Ok(())
}
}
pub struct LayoutLineBuildIter<'a> {
line: &'a LogicalLine,
logical_iter: LogicalLineIter<'a>,
physical_iter: Option<PhysicalLineIter<'a>>,
logical_offset: glm::TVec2<f32>,
physical_offset: glm::TVec2<f32>,
}
impl<'a> LayoutLineBuildIter<'a> {
fn new(line: &'a LogicalLine, logical_offset: glm::TVec2<f32>) -> Self {
let canvas_size = line.info.canvas.read().size;
let mut logical_iter = line.physical_lines();
let physical_iter = logical_iter.next();
let physical_offset = match &physical_iter {
Some(p_line) => glm::vec2(
line.info.alignment.horizontal.offset(p_line.width(), canvas_size.x),
0.0,
),
None => glm::vec2(0.0, 0.0),
};
Self {
line,
logical_iter,
physical_iter,
logical_offset,
physical_offset,
}
}
}
impl Iterator for LayoutLineBuildIter<'_> {
type Item = PositionedChar;
fn next(&mut self) -> Option<Self::Item> {
let info = &self.line.info;
let (canvas_offset, canvas_size) = {
let canvas = info.canvas.read();
(canvas.offset, canvas.size)
};
let maybe_c = self.physical_iter.as_mut()?.next();
let c = match maybe_c {
Some(c) => c,
None => {
self.physical_iter = self.logical_iter.next();
self.physical_offset = glm::vec2(
match &self.physical_iter {
Some(p_line) => info.alignment.horizontal.offset(
p_line.width(),
canvas_size.x,
),
None => 0.0,
},
self.physical_offset.y + info.sizer.v_advance(info.size),
);
self.physical_iter.as_mut()?.next()?
}
};
Some(PositionedChar {
value: c.inner.value,
position: canvas_offset + self.logical_offset + self.physical_offset + glm::vec2(c.x, 0.0),
size: info.size,
color: c.inner.color,
})
}
}
#[derive(Debug)]
pub struct TextLayout {
info: Arc<TextLayoutInfo>,
lines: Vec<LogicalLine>,
cursor: glm::TVec2<usize>,
color: glm::TVec3<f32>,
cache_width: OnceCell<f32>,
cache_height: OnceCell<f32>,
}
impl TextLayout {
pub fn new(
sizer: Arc<dyn FontSizer>,
create_info: TextLayoutCreateInfo,
scale: f32,
) -> Self {
let mut layout = Self {
info: Arc::new(create_info.into_info(sizer, scale)),
lines: vec![],
cursor: glm::vec2(0, 0),
color: glm::vec3(1.0, 1.0, 1.0),
cache_width: OnceCell::new(),
cache_height: OnceCell::new(),
};
layout.lines.push(LogicalLine::new(layout.info.clone()));
layout
}
pub fn for_render_target(
target: &Arc<dyn RenderTarget>,
sizer: Arc<dyn FontSizer>,
mut create_info: TextLayoutCreateInfo,
) -> Self {
if create_info.canvas_size.is_none() {
create_info.canvas_size = Some(target.extent().cast::<f32>());
}
Self::new(sizer, create_info, target.scale_factor() as f32)
}
fn invalidate_size_caches(&mut self) {
self.cache_width.take();
self.cache_height.take();
}
#[inline]
pub fn cursor(&self) -> &glm::TVec2<usize> { &self.cursor }
#[inline]
pub fn lines(&self) -> slice::Iter<'_, LogicalLine> {
self.lines.iter()
}
pub fn color(&mut self, color: glm::TVec3<f32>) -> &mut Self {
self.color = color;
self
}
pub fn move_cursor_to(&mut self, pos: glm::TVec2<usize>) -> &mut Self {
self.cursor.y = min(pos.y, self.lines.len());
self.cursor.x = min(pos.x, self.lines[self.cursor.y].chars.len());
self
}
pub fn move_cursor_left(&mut self) -> &mut Self {
if self.cursor.x == 0 {
if self.cursor.y > 0 {
self.cursor.y -= 1;
self.cursor.x = self.lines[self.cursor.y].chars.len();
}
} else {
self.cursor.x -= 1;
}
self
}
pub fn move_cursor_right(&mut self) -> &mut Self {
if self.cursor.x >= self.lines[self.cursor.y].chars.len() {
if self.cursor.y < self.lines.len() - 1 {
self.cursor.y += 1;
self.cursor.x = 0;
}
} else {
self.cursor.x += 1;
}
self
}
pub fn move_cursor_up(&mut self) -> &mut Self {
let overflow_x = self.lines[self.cursor.y].move_cursor_up(&mut self.cursor);
if let Some(overflow_x) = overflow_x {
let line = &self.lines[self.cursor.y];
self.cursor.x = line.index_for_x(overflow_x, line.line_count() - 1);
}
self
}
pub fn move_cursor_down(&mut self) -> &mut Self {
let overflow_x = self.lines[self.cursor.y]
.move_cursor_down(&mut self.cursor, self.lines.len());
if let Some(overflow_x) = overflow_x {
let line = &self.lines[self.cursor.y];
self.cursor.x = line.index_for_x(overflow_x, 0);
}
self
}
pub fn move_cursor_home(&mut self) -> &mut Self {
self.cursor.x = 0;
self
}
pub fn move_cursor_end(&mut self) -> &mut Self {
self.cursor.x = self.lines[self.cursor.y].chars.len();
self
}
pub fn newline(&mut self) -> &mut Self {
if let Some(max_line_count) = self.info.max_line_count {
if self.lines.len() >= max_line_count {
return self;
}
}
let remainder = self.lines[self.cursor.y].split_off(self.info.clone(), self.cursor.x);
self.lines.insert(self.cursor.y + 1, remainder);
self.cursor = glm::vec2(0, self.cursor.y + 1);
self.invalidate_size_caches();
self
}
pub fn delete_char(&mut self) -> &mut Self {
if self.cursor.x < self.lines[self.cursor.y].len() {
self.lines[self.cursor.y].delete(self.cursor.x);
}
self.invalidate_size_caches();
self
}
pub fn clear(&mut self) -> &mut Self {
self.lines = vec![LogicalLine::new(self.info.clone())];
self.cursor = glm::vec2(0, 0);
self.invalidate_size_caches();
self
}
#[inline]
pub fn info(&self) -> &TextLayoutInfo { self.info.as_ref() }
#[inline]
pub fn width(&self) -> f32 {
*self.cache_width.get_or_init(|| {
self.lines()
.map(|line| line.width())
.fold(0.0, f32::max)
})
}
#[inline]
pub fn height(&self) -> f32 {
*self.cache_height.get_or_init(|| {
self.lines().map(|line| line.height()).sum::<f32>()
+ self.info.sizer.line_gap(self.info.size) * (self.lines.len() - 1) as f32
})
}
pub fn text(&mut self, text: &str) -> &mut Self {
let mut first = true;
for substring in text.split('\n') {
if !first { self.newline(); }
self.lines[self.cursor.y].insert(self.cursor.x, substring, self.color);
self.cursor.x += substring.chars().count();
first = false;
}
self.invalidate_size_caches();
self
}
pub fn text_layout(&mut self, other: &TextLayout) -> &mut Self {
let mut first = true;
for line in &other.lines {
if !first { self.newline(); }
self.cursor.x += self.lines[self.cursor.y]
.insert_buffer(self.cursor.x, line.physical_lines().flatten());
first = false;
}
self.invalidate_size_caches();
self
}
pub fn resize(&mut self, canvas_offset: glm::TVec2<f32>, canvas_size: glm::TVec2<f32>) {
{
let mut canvas = self.info.canvas.write();
canvas.offset = canvas_offset;
canvas.size = canvas_size;
}
for line in &mut self.lines {
line.adjust_trailing(0);
}
self.invalidate_size_caches();
}
#[cfg(feature = "gui")]
pub fn handle_input_event(&mut self, event: &crate::gui::input::InputDelegateEvent) {
use crate::gui::input::{ElementState, InputDelegateEvent, LogicalKey};
use winit::keyboard::NamedKey;
match event {
InputDelegateEvent::Key(event) => {
if event.state == ElementState::Pressed {
match &event.logical_key {
LogicalKey::Named(NamedKey::Home) => { self.move_cursor_home(); },
LogicalKey::Named(NamedKey::End) => { self.move_cursor_end(); },
LogicalKey::Named(NamedKey::Enter) => { self.newline(); },
LogicalKey::Named(NamedKey::Backspace) => {
if self.cursor != glm::vec2(0, 0) {
self.move_cursor_left();
self.delete_char();
}
},
LogicalKey::Named(NamedKey::Delete) => { self.delete_char(); },
LogicalKey::Named(NamedKey::Space) => { self.text(" "); },
LogicalKey::Character(c) => { self.text(c); },
_ => {}
}
}
},
_ => {}
}
}
#[inline]
pub fn iter_build(&self) -> TextLayoutBuildIter<'_> {
TextLayoutBuildIter::new(self)
}
}
impl Display for TextLayout {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let mut first = true;
for line in &self.lines {
if !first { f.write_char('\n')?; }
Display::fmt(line, f)?;
first = false;
}
Ok(())
}
}
pub struct TextLayoutBuildIter<'a> {
layout: &'a TextLayout,
lines_iter: slice::Iter<'a, LogicalLine>,
sub_iter: Option<LayoutLineBuildIter<'a>>,
offset: glm::TVec2<f32>,
}
impl<'a> TextLayoutBuildIter<'a> {
fn new(layout: &'a TextLayout) -> Self {
let offset = glm::vec2(
0.0,
match layout.info.alignment.vertical {
LayoutVerticalAlignment::Top => 0.0,
LayoutVerticalAlignment::Center =>
(layout.info.canvas.read().size.y - layout.height()) / 2.0,
LayoutVerticalAlignment::Bottom =>
layout.info.canvas.read().size.y - layout.height(),
}
);
let mut lines_iter = layout.lines();
let sub_iter = lines_iter.next()
.map(|line| line.iter_build(offset));
Self {
layout,
lines_iter,
sub_iter,
offset,
}
}
}
impl Iterator for TextLayoutBuildIter<'_> {
type Item = PositionedChar;
fn next(&mut self) -> Option<Self::Item> {
let mut maybe_c: Option<Self::Item> = None;
while maybe_c.is_none() {
let sub_iter = self.sub_iter.as_mut()?;
maybe_c = sub_iter.next();
if maybe_c.is_none() {
self.offset.y += sub_iter.line.height()
+ self.layout.info.sizer.line_gap(self.layout.info.size);
self.sub_iter = self.lines_iter.next()
.map(|line| line.iter_build(self.offset));
}
}
maybe_c
}
}