Move LayoutRun back to buffer module
This commit is contained in:
parent
1fca62bdc3
commit
02137a7561
2 changed files with 164 additions and 163 deletions
164
src/buffer.rs
164
src/buffer.rs
|
|
@ -11,10 +11,170 @@ use unicode_segmentation::UnicodeSegmentation;
|
|||
|
||||
use crate::{
|
||||
render_decoration, Affinity, Align, Attrs, AttrsList, BidiParagraphs, BorrowedWithFontSystem,
|
||||
BufferLine, Color, Cursor, Ellipsize, FontSystem, Hinting, LayoutCursor, LayoutLine,
|
||||
LayoutRunIter, LineEnding, LineIter, Motion, Renderer, Scroll, ShapeLine, Shaping, Wrap,
|
||||
BufferLine, Color, Cursor, DecorationSpan, Ellipsize, FontSystem, Hinting, LayoutCursor,
|
||||
LayoutGlyph, LayoutLine, LineEnding, LineIter, Motion, Renderer, Scroll, ShapeLine, Shaping,
|
||||
Wrap,
|
||||
};
|
||||
|
||||
/// A line of visible text for rendering
|
||||
#[derive(Debug)]
|
||||
pub struct LayoutRun<'a> {
|
||||
/// The index of the original text line
|
||||
pub line_i: usize,
|
||||
/// The original text line
|
||||
pub text: &'a str,
|
||||
/// True if the original paragraph direction is RTL
|
||||
pub rtl: bool,
|
||||
/// The array of layout glyphs to draw
|
||||
pub glyphs: &'a [LayoutGlyph],
|
||||
/// Text decoration spans covering ranges of glyphs
|
||||
pub decorations: &'a [DecorationSpan],
|
||||
/// Y offset to baseline of line
|
||||
pub line_y: f32,
|
||||
/// Y offset to top of line
|
||||
pub line_top: f32,
|
||||
/// Y offset to next line
|
||||
pub line_height: f32,
|
||||
/// Width of line
|
||||
pub line_w: f32,
|
||||
}
|
||||
|
||||
impl LayoutRun<'_> {
|
||||
/// Return the pixel span `Some((x_left, x_width))` of the highlighted area between `cursor_start`
|
||||
/// and `cursor_end` within this run, or None if the cursor range does not intersect this run.
|
||||
/// This may return widths of zero if `cursor_start == cursor_end`, if the run is empty, or if the
|
||||
/// region's left start boundary is the same as the cursor's end boundary or vice versa.
|
||||
#[allow(clippy::missing_panics_doc)]
|
||||
pub fn highlight(&self, cursor_start: Cursor, cursor_end: Cursor) -> Option<(f32, f32)> {
|
||||
let mut x_start = None;
|
||||
let mut x_end = None;
|
||||
let rtl_factor = if self.rtl { 1. } else { 0. };
|
||||
let ltr_factor = 1. - rtl_factor;
|
||||
for glyph in self.glyphs {
|
||||
let cursor = self.cursor_from_glyph_left(glyph);
|
||||
if cursor >= cursor_start && cursor <= cursor_end {
|
||||
if x_start.is_none() {
|
||||
x_start = Some(glyph.x + glyph.w.mul_add(rtl_factor, 0.0));
|
||||
}
|
||||
x_end = Some(glyph.x + glyph.w.mul_add(rtl_factor, 0.0));
|
||||
}
|
||||
let cursor = self.cursor_from_glyph_right(glyph);
|
||||
if cursor >= cursor_start && cursor <= cursor_end {
|
||||
if x_start.is_none() {
|
||||
x_start = Some(glyph.x + glyph.w.mul_add(ltr_factor, 0.0));
|
||||
}
|
||||
x_end = Some(glyph.x + glyph.w.mul_add(ltr_factor, 0.0));
|
||||
}
|
||||
}
|
||||
x_start.map(|x_start| {
|
||||
let x_end = x_end.expect("end of cursor not found");
|
||||
let (x_start, x_end) = if x_start < x_end {
|
||||
(x_start, x_end)
|
||||
} else {
|
||||
(x_end, x_start)
|
||||
};
|
||||
(x_start, x_end - x_start)
|
||||
})
|
||||
}
|
||||
|
||||
const fn cursor_from_glyph_left(&self, glyph: &LayoutGlyph) -> Cursor {
|
||||
if self.rtl {
|
||||
Cursor::new_with_affinity(self.line_i, glyph.end, Affinity::Before)
|
||||
} else {
|
||||
Cursor::new_with_affinity(self.line_i, glyph.start, Affinity::After)
|
||||
}
|
||||
}
|
||||
|
||||
const fn cursor_from_glyph_right(&self, glyph: &LayoutGlyph) -> Cursor {
|
||||
if self.rtl {
|
||||
Cursor::new_with_affinity(self.line_i, glyph.start, Affinity::After)
|
||||
} else {
|
||||
Cursor::new_with_affinity(self.line_i, glyph.end, Affinity::Before)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator of visible text lines, see [`LayoutRun`]
|
||||
#[derive(Debug)]
|
||||
pub struct LayoutRunIter<'b> {
|
||||
lines: &'b [BufferLine],
|
||||
height_opt: Option<f32>,
|
||||
line_height: f32,
|
||||
scroll: f32,
|
||||
line_i: usize,
|
||||
layout_i: usize,
|
||||
total_height: f32,
|
||||
line_top: f32,
|
||||
}
|
||||
|
||||
impl<'b> LayoutRunIter<'b> {
|
||||
pub const fn new(
|
||||
lines: &'b [BufferLine],
|
||||
height_opt: Option<f32>,
|
||||
line_height: f32,
|
||||
scroll: f32,
|
||||
start: usize,
|
||||
) -> Self {
|
||||
Self {
|
||||
lines,
|
||||
height_opt,
|
||||
line_height,
|
||||
scroll,
|
||||
line_i: start,
|
||||
layout_i: 0,
|
||||
total_height: 0.0,
|
||||
line_top: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> Iterator for LayoutRunIter<'b> {
|
||||
type Item = LayoutRun<'b>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while let Some(line) = self.lines.get(self.line_i) {
|
||||
let shape = line.shape_opt()?;
|
||||
let layout = line.layout_opt()?;
|
||||
while let Some(layout_line) = layout.get(self.layout_i) {
|
||||
self.layout_i += 1;
|
||||
|
||||
let line_height = layout_line.line_height_opt.unwrap_or(self.line_height);
|
||||
self.total_height += line_height;
|
||||
|
||||
let line_top = self.line_top - self.scroll;
|
||||
let glyph_height = layout_line.max_ascent + layout_line.max_descent;
|
||||
let centering_offset = (line_height - glyph_height) / 2.0;
|
||||
let line_y = line_top + centering_offset + layout_line.max_ascent;
|
||||
if let Some(height) = self.height_opt {
|
||||
if line_y - layout_line.max_ascent > height {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
self.line_top += line_height;
|
||||
if line_y + layout_line.max_descent < 0.0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
return Some(LayoutRun {
|
||||
line_i: self.line_i,
|
||||
text: line.text(),
|
||||
rtl: shape.rtl,
|
||||
glyphs: &layout_line.glyphs,
|
||||
decorations: &layout_line.decorations,
|
||||
line_y,
|
||||
line_top,
|
||||
line_height,
|
||||
line_w: layout_line.w,
|
||||
});
|
||||
}
|
||||
self.line_i += 1;
|
||||
self.layout_i = 0;
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Metrics of text
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
pub struct Metrics {
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ use alloc::{string::String, vec::Vec};
|
|||
use core::mem;
|
||||
|
||||
use crate::{
|
||||
Affinity, Align, Attrs, AttrsList, Cached, Cursor, DecorationSpan, Ellipsize, FontSystem,
|
||||
Hinting, LayoutGlyph, LayoutLine, LineEnding, ShapeLine, Shaping, Wrap,
|
||||
Align, Attrs, AttrsList, Cached, Ellipsize, FontSystem, Hinting, LayoutLine, LayoutRunIter,
|
||||
LineEnding, ShapeLine, Shaping, Wrap,
|
||||
};
|
||||
|
||||
/// A line (or paragraph) of text that is shaped and laid out
|
||||
|
|
@ -325,162 +325,3 @@ impl BufferLine {
|
|||
text
|
||||
}
|
||||
}
|
||||
|
||||
/// A line of visible text for rendering
|
||||
#[derive(Debug)]
|
||||
pub struct LayoutRun<'a> {
|
||||
/// The index of the original text line
|
||||
pub line_i: usize,
|
||||
/// The original text line
|
||||
pub text: &'a str,
|
||||
/// True if the original paragraph direction is RTL
|
||||
pub rtl: bool,
|
||||
/// The array of layout glyphs to draw
|
||||
pub glyphs: &'a [LayoutGlyph],
|
||||
/// Text decoration spans covering ranges of glyphs
|
||||
pub decorations: &'a [DecorationSpan],
|
||||
/// Y offset to baseline of line
|
||||
pub line_y: f32,
|
||||
/// Y offset to top of line
|
||||
pub line_top: f32,
|
||||
/// Y offset to next line
|
||||
pub line_height: f32,
|
||||
/// Width of line
|
||||
pub line_w: f32,
|
||||
}
|
||||
|
||||
impl LayoutRun<'_> {
|
||||
/// Return the pixel span `Some((x_left, x_width))` of the highlighted area between `cursor_start`
|
||||
/// and `cursor_end` within this run, or None if the cursor range does not intersect this run.
|
||||
/// This may return widths of zero if `cursor_start == cursor_end`, if the run is empty, or if the
|
||||
/// region's left start boundary is the same as the cursor's end boundary or vice versa.
|
||||
#[allow(clippy::missing_panics_doc)]
|
||||
pub fn highlight(&self, cursor_start: Cursor, cursor_end: Cursor) -> Option<(f32, f32)> {
|
||||
let mut x_start = None;
|
||||
let mut x_end = None;
|
||||
let rtl_factor = if self.rtl { 1. } else { 0. };
|
||||
let ltr_factor = 1. - rtl_factor;
|
||||
for glyph in self.glyphs {
|
||||
let cursor = self.cursor_from_glyph_left(glyph);
|
||||
if cursor >= cursor_start && cursor <= cursor_end {
|
||||
if x_start.is_none() {
|
||||
x_start = Some(glyph.x + glyph.w.mul_add(rtl_factor, 0.0));
|
||||
}
|
||||
x_end = Some(glyph.x + glyph.w.mul_add(rtl_factor, 0.0));
|
||||
}
|
||||
let cursor = self.cursor_from_glyph_right(glyph);
|
||||
if cursor >= cursor_start && cursor <= cursor_end {
|
||||
if x_start.is_none() {
|
||||
x_start = Some(glyph.x + glyph.w.mul_add(ltr_factor, 0.0));
|
||||
}
|
||||
x_end = Some(glyph.x + glyph.w.mul_add(ltr_factor, 0.0));
|
||||
}
|
||||
}
|
||||
x_start.map(|x_start| {
|
||||
let x_end = x_end.expect("end of cursor not found");
|
||||
let (x_start, x_end) = if x_start < x_end {
|
||||
(x_start, x_end)
|
||||
} else {
|
||||
(x_end, x_start)
|
||||
};
|
||||
(x_start, x_end - x_start)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) const fn cursor_from_glyph_left(&self, glyph: &LayoutGlyph) -> Cursor {
|
||||
if self.rtl {
|
||||
Cursor::new_with_affinity(self.line_i, glyph.end, Affinity::Before)
|
||||
} else {
|
||||
Cursor::new_with_affinity(self.line_i, glyph.start, Affinity::After)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) const fn cursor_from_glyph_right(&self, glyph: &LayoutGlyph) -> Cursor {
|
||||
if self.rtl {
|
||||
Cursor::new_with_affinity(self.line_i, glyph.start, Affinity::After)
|
||||
} else {
|
||||
Cursor::new_with_affinity(self.line_i, glyph.end, Affinity::Before)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator of visible text lines, see [`LayoutRun`]
|
||||
#[derive(Debug)]
|
||||
pub struct LayoutRunIter<'b> {
|
||||
lines: &'b [BufferLine],
|
||||
height_opt: Option<f32>,
|
||||
line_height: f32,
|
||||
scroll: f32,
|
||||
line_i: usize,
|
||||
layout_i: usize,
|
||||
total_height: f32,
|
||||
line_top: f32,
|
||||
}
|
||||
|
||||
impl<'b> LayoutRunIter<'b> {
|
||||
pub const fn new(
|
||||
lines: &'b [BufferLine],
|
||||
height_opt: Option<f32>,
|
||||
line_height: f32,
|
||||
scroll: f32,
|
||||
start: usize,
|
||||
) -> Self {
|
||||
Self {
|
||||
lines,
|
||||
height_opt,
|
||||
line_height,
|
||||
scroll,
|
||||
line_i: start,
|
||||
layout_i: 0,
|
||||
total_height: 0.0,
|
||||
line_top: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'b> Iterator for LayoutRunIter<'b> {
|
||||
type Item = LayoutRun<'b>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while let Some(line) = self.lines.get(self.line_i) {
|
||||
let shape = line.shape_opt()?;
|
||||
let layout = line.layout_opt()?;
|
||||
while let Some(layout_line) = layout.get(self.layout_i) {
|
||||
self.layout_i += 1;
|
||||
|
||||
let line_height = layout_line.line_height_opt.unwrap_or(self.line_height);
|
||||
self.total_height += line_height;
|
||||
|
||||
let line_top = self.line_top - self.scroll;
|
||||
let glyph_height = layout_line.max_ascent + layout_line.max_descent;
|
||||
let centering_offset = (line_height - glyph_height) / 2.0;
|
||||
let line_y = line_top + centering_offset + layout_line.max_ascent;
|
||||
if let Some(height) = self.height_opt {
|
||||
if line_y - layout_line.max_ascent > height {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
self.line_top += line_height;
|
||||
if line_y + layout_line.max_descent < 0.0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
return Some(LayoutRun {
|
||||
line_i: self.line_i,
|
||||
text: line.text(),
|
||||
rtl: shape.rtl,
|
||||
glyphs: &layout_line.glyphs,
|
||||
decorations: &layout_line.decorations,
|
||||
line_y,
|
||||
line_top,
|
||||
line_height,
|
||||
line_w: layout_line.w,
|
||||
});
|
||||
}
|
||||
self.line_i += 1;
|
||||
self.layout_i = 0;
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue