2022-10-24 08:56:48 -06:00
|
|
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
|
|
|
|
|
2022-11-08 08:43:27 -07:00
|
|
|
#[cfg(not(feature = "std"))]
|
|
|
|
|
use alloc::{
|
|
|
|
|
string::{String, ToString},
|
|
|
|
|
vec::Vec,
|
|
|
|
|
};
|
2023-01-04 20:03:03 -07:00
|
|
|
use core::{cmp, fmt};
|
2022-10-24 14:18:40 -06:00
|
|
|
use unicode_segmentation::UnicodeSegmentation;
|
2022-10-18 12:07:22 -06:00
|
|
|
|
2022-11-08 08:43:27 -07:00
|
|
|
#[cfg(feature = "swash")]
|
|
|
|
|
use crate::Color;
|
2023-03-12 10:30:03 +01:00
|
|
|
use crate::{
|
|
|
|
|
Attrs, AttrsList, BorrowedWithFontSystem, BufferLine, FontSystem, LayoutGlyph, LayoutLine,
|
|
|
|
|
ShapeLine, Wrap,
|
|
|
|
|
};
|
2022-10-18 12:07:22 -06:00
|
|
|
|
2022-10-18 17:13:48 -06:00
|
|
|
/// Current cursor location
|
2022-12-30 00:15:00 -08:00
|
|
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
|
2022-10-31 11:24:36 -06:00
|
|
|
pub struct Cursor {
|
2022-10-20 19:34:56 -06:00
|
|
|
/// Text line the cursor is on
|
2022-10-26 12:23:03 -06:00
|
|
|
pub line: usize,
|
2023-01-02 17:29:22 -08:00
|
|
|
/// First-byte-index of glyph at cursor (will insert behind this glyph)
|
2022-10-21 11:44:11 -06:00
|
|
|
pub index: usize,
|
2023-01-14 10:28:34 -07:00
|
|
|
/// Whether to associate the cursor with the run before it or the run after it if placed at the
|
|
|
|
|
/// boundary between two runs
|
2023-01-14 10:25:13 -07:00
|
|
|
pub affinity: Affinity,
|
2022-10-18 12:07:22 -06:00
|
|
|
}
|
|
|
|
|
|
2022-10-31 11:24:36 -06:00
|
|
|
impl Cursor {
|
2022-10-27 10:29:19 -06:00
|
|
|
/// Create a new cursor
|
2022-10-26 12:23:03 -06:00
|
|
|
pub const fn new(line: usize, index: usize) -> Self {
|
2023-01-14 10:25:13 -07:00
|
|
|
Self::new_with_affinity(line, index, Affinity::Before)
|
2023-01-10 20:12:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Create a new cursor, specifying the affinity
|
2023-01-14 10:25:13 -07:00
|
|
|
pub const fn new_with_affinity(line: usize, index: usize, affinity: Affinity) -> Self {
|
2023-01-10 20:12:12 -07:00
|
|
|
Self {
|
|
|
|
|
line,
|
|
|
|
|
index,
|
|
|
|
|
affinity,
|
|
|
|
|
}
|
2022-10-18 12:07:22 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-29 22:29:17 -03:30
|
|
|
/// Whether to associate cursors placed at a boundary between runs with the run before or after it.
|
2023-01-14 10:25:13 -07:00
|
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
|
|
|
|
pub enum Affinity {
|
|
|
|
|
Before,
|
|
|
|
|
After,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Affinity {
|
|
|
|
|
pub fn before(&self) -> bool {
|
|
|
|
|
*self == Self::Before
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn after(&self) -> bool {
|
|
|
|
|
*self == Self::After
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn from_before(before: bool) -> Self {
|
|
|
|
|
if before {
|
|
|
|
|
Self::Before
|
|
|
|
|
} else {
|
|
|
|
|
Self::After
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn from_after(after: bool) -> Self {
|
|
|
|
|
if after {
|
|
|
|
|
Self::After
|
|
|
|
|
} else {
|
|
|
|
|
Self::Before
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for Affinity {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Affinity::Before
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-29 22:29:17 -03:30
|
|
|
/// The position of a cursor within a [`Buffer`].
|
2022-10-31 11:24:36 -06:00
|
|
|
pub struct LayoutCursor {
|
|
|
|
|
pub line: usize,
|
|
|
|
|
pub layout: usize,
|
|
|
|
|
pub glyph: usize,
|
2022-10-22 11:42:08 -06:00
|
|
|
}
|
|
|
|
|
|
2022-10-31 11:24:36 -06:00
|
|
|
impl LayoutCursor {
|
|
|
|
|
pub fn new(line: usize, layout: usize, glyph: usize) -> Self {
|
2023-01-04 20:03:03 -07:00
|
|
|
Self {
|
|
|
|
|
line,
|
|
|
|
|
layout,
|
|
|
|
|
glyph,
|
|
|
|
|
}
|
2022-10-22 11:42:08 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-27 10:29:19 -06:00
|
|
|
/// A line of visible text for rendering
|
2022-10-31 11:24:36 -06:00
|
|
|
pub struct LayoutRun<'a> {
|
2022-10-25 12:52:46 -06:00
|
|
|
/// The index of the original text line
|
2022-10-26 12:23:03 -06:00
|
|
|
pub line_i: usize,
|
2022-10-25 12:52:46 -06:00
|
|
|
/// The original text line
|
|
|
|
|
pub text: &'a str,
|
2022-10-25 13:15:52 -06:00
|
|
|
/// True if the original paragraph direction is RTL
|
2022-10-25 12:52:46 -06:00
|
|
|
pub rtl: bool,
|
|
|
|
|
/// The array of layout glyphs to draw
|
|
|
|
|
pub glyphs: &'a [LayoutGlyph],
|
|
|
|
|
/// Y offset of line
|
2023-02-04 11:13:53 +01:00
|
|
|
pub line_y: f32,
|
2022-12-15 04:59:31 +01:00
|
|
|
/// width of line
|
|
|
|
|
pub line_w: f32,
|
2022-10-25 12:52:46 -06:00
|
|
|
}
|
|
|
|
|
|
2023-01-02 17:29:22 -08:00
|
|
|
impl<'a> LayoutRun<'a> {
|
2023-02-28 19:42:53 +01:00
|
|
|
/// 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
|
2023-01-02 17:29:22 -08:00
|
|
|
/// region's left start boundary is the same as the cursor's end boundary or vice versa.
|
|
|
|
|
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.iter() {
|
2023-01-10 20:12:12 -07:00
|
|
|
let cursor = self.cursor_from_glyph_left(glyph);
|
2023-01-02 17:29:22 -08:00
|
|
|
if cursor >= cursor_start && cursor <= cursor_end {
|
|
|
|
|
if x_start.is_none() {
|
|
|
|
|
x_start = Some(glyph.x + glyph.w * rtl_factor);
|
|
|
|
|
}
|
|
|
|
|
x_end = Some(glyph.x + glyph.w * rtl_factor);
|
|
|
|
|
}
|
2023-01-10 20:12:12 -07:00
|
|
|
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 * ltr_factor);
|
|
|
|
|
}
|
|
|
|
|
x_end = Some(glyph.x + glyph.w * ltr_factor);
|
2023-01-02 17:29:22 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if let Some(x_start) = x_start {
|
2023-01-04 20:02:00 -07:00
|
|
|
let x_end = x_end.expect("end of cursor not found");
|
2023-01-04 20:03:03 -07:00
|
|
|
let (x_start, x_end) = if x_start < x_end {
|
|
|
|
|
(x_start, x_end)
|
|
|
|
|
} else {
|
|
|
|
|
(x_end, x_start)
|
|
|
|
|
};
|
2023-01-02 17:29:22 -08:00
|
|
|
Some((x_start, x_end - x_start))
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-10 20:12:12 -07:00
|
|
|
|
|
|
|
|
fn cursor_from_glyph_left(&self, glyph: &LayoutGlyph) -> Cursor {
|
|
|
|
|
if self.rtl {
|
2023-01-14 10:25:13 -07:00
|
|
|
Cursor::new_with_affinity(self.line_i, glyph.end, Affinity::Before)
|
2023-01-10 20:12:12 -07:00
|
|
|
} else {
|
2023-01-14 10:25:13 -07:00
|
|
|
Cursor::new_with_affinity(self.line_i, glyph.start, Affinity::After)
|
2023-01-10 20:12:12 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn cursor_from_glyph_right(&self, glyph: &LayoutGlyph) -> Cursor {
|
|
|
|
|
if self.rtl {
|
2023-01-14 10:25:13 -07:00
|
|
|
Cursor::new_with_affinity(self.line_i, glyph.start, Affinity::After)
|
2023-01-10 20:12:12 -07:00
|
|
|
} else {
|
2023-01-14 10:25:13 -07:00
|
|
|
Cursor::new_with_affinity(self.line_i, glyph.end, Affinity::Before)
|
2023-01-10 20:12:12 -07:00
|
|
|
}
|
|
|
|
|
}
|
2023-01-02 17:29:22 -08:00
|
|
|
}
|
|
|
|
|
|
2022-11-15 12:26:59 -07:00
|
|
|
/// An iterator of visible text lines, see [`LayoutRun`]
|
2023-03-12 10:30:03 +01:00
|
|
|
pub struct LayoutRunIter<'b> {
|
|
|
|
|
buffer: &'b Buffer,
|
2022-10-25 12:52:46 -06:00
|
|
|
line_i: usize,
|
|
|
|
|
layout_i: usize,
|
2022-12-28 11:21:14 -08:00
|
|
|
remaining_len: usize,
|
2023-02-04 11:13:53 +01:00
|
|
|
line_y: f32,
|
2022-10-25 12:52:46 -06:00
|
|
|
total_layout: i32,
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-12 10:30:03 +01:00
|
|
|
impl<'b> LayoutRunIter<'b> {
|
|
|
|
|
pub fn new(buffer: &'b Buffer) -> Self {
|
2023-01-04 20:03:03 -07:00
|
|
|
let total_layout_lines: usize = buffer
|
|
|
|
|
.lines
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|line| {
|
|
|
|
|
line.layout_opt()
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|layout| layout.len())
|
|
|
|
|
.unwrap_or_default()
|
|
|
|
|
})
|
|
|
|
|
.sum();
|
|
|
|
|
let top_cropped_layout_lines =
|
|
|
|
|
total_layout_lines.saturating_sub(buffer.scroll.try_into().unwrap_or_default());
|
2023-02-28 20:39:59 +01:00
|
|
|
let maximum_lines = if buffer.metrics.line_height == 0.0 {
|
|
|
|
|
0
|
|
|
|
|
} else {
|
|
|
|
|
(buffer.height / buffer.metrics.line_height) as i32
|
|
|
|
|
};
|
2023-01-04 20:03:03 -07:00
|
|
|
let bottom_cropped_layout_lines =
|
|
|
|
|
if top_cropped_layout_lines > maximum_lines.try_into().unwrap_or_default() {
|
|
|
|
|
maximum_lines.try_into().unwrap_or_default()
|
|
|
|
|
} else {
|
|
|
|
|
top_cropped_layout_lines
|
|
|
|
|
};
|
2023-02-04 11:13:53 +01:00
|
|
|
|
2022-10-25 12:52:46 -06:00
|
|
|
Self {
|
|
|
|
|
buffer,
|
|
|
|
|
line_i: 0,
|
|
|
|
|
layout_i: 0,
|
2023-02-28 19:42:53 +01:00
|
|
|
remaining_len: bottom_cropped_layout_lines,
|
2023-02-24 11:27:45 -03:30
|
|
|
line_y: buffer.metrics.y_offset(),
|
2022-10-25 12:52:46 -06:00
|
|
|
total_layout: 0,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-12 10:30:03 +01:00
|
|
|
impl<'b> Iterator for LayoutRunIter<'b> {
|
2022-10-31 11:24:36 -06:00
|
|
|
type Item = LayoutRun<'b>;
|
2022-10-25 12:52:46 -06:00
|
|
|
|
2022-12-28 11:21:14 -08:00
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
|
|
(self.remaining_len, Some(self.remaining_len))
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-25 12:52:46 -06:00
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
|
while let Some(line) = self.buffer.lines.get(self.line_i) {
|
2022-10-27 17:39:26 -06:00
|
|
|
let shape = line.shape_opt().as_ref()?;
|
|
|
|
|
let layout = line.layout_opt().as_ref()?;
|
2022-10-25 12:52:46 -06:00
|
|
|
while let Some(layout_line) = layout.get(self.layout_i) {
|
|
|
|
|
self.layout_i += 1;
|
|
|
|
|
|
|
|
|
|
let scrolled = self.total_layout < self.buffer.scroll;
|
|
|
|
|
self.total_layout += 1;
|
|
|
|
|
if scrolled {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.line_y += self.buffer.metrics.line_height;
|
2023-02-24 11:27:45 -03:30
|
|
|
if self.line_y - self.buffer.metrics.y_offset() > self.buffer.height {
|
2022-10-25 12:52:46 -06:00
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-28 16:30:53 -06:00
|
|
|
return self.remaining_len.checked_sub(1).map(|num| {
|
|
|
|
|
self.remaining_len = num;
|
|
|
|
|
LayoutRun {
|
|
|
|
|
line_i: self.line_i,
|
|
|
|
|
text: line.text(),
|
|
|
|
|
rtl: shape.rtl,
|
|
|
|
|
glyphs: &layout_line.glyphs,
|
|
|
|
|
line_y: self.line_y,
|
|
|
|
|
line_w: layout_line.w,
|
|
|
|
|
}
|
2022-10-25 12:52:46 -06:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
self.line_i += 1;
|
|
|
|
|
self.layout_i = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
None
|
|
|
|
|
}
|
2022-10-25 11:40:10 -06:00
|
|
|
}
|
|
|
|
|
|
2023-03-12 10:30:03 +01:00
|
|
|
impl<'b> ExactSizeIterator for LayoutRunIter<'b> {}
|
2022-12-28 11:21:14 -08:00
|
|
|
|
2022-10-18 17:13:48 -06:00
|
|
|
/// Metrics of text
|
2023-02-04 11:13:53 +01:00
|
|
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
2022-10-31 11:24:36 -06:00
|
|
|
pub struct Metrics {
|
2022-10-18 17:13:48 -06:00
|
|
|
/// Font size in pixels
|
2023-02-04 11:13:53 +01:00
|
|
|
pub font_size: f32,
|
2022-10-18 17:13:48 -06:00
|
|
|
/// Line height in pixels
|
2023-02-04 11:13:53 +01:00
|
|
|
pub line_height: f32,
|
2022-10-18 17:04:22 -06:00
|
|
|
}
|
|
|
|
|
|
2022-10-31 11:24:36 -06:00
|
|
|
impl Metrics {
|
2023-02-04 11:13:53 +01:00
|
|
|
pub const fn new(font_size: f32, line_height: f32) -> Self {
|
2023-01-04 20:03:03 -07:00
|
|
|
Self {
|
|
|
|
|
font_size,
|
|
|
|
|
line_height,
|
|
|
|
|
}
|
2022-10-18 17:04:22 -06:00
|
|
|
}
|
|
|
|
|
|
2023-02-04 11:13:53 +01:00
|
|
|
pub fn scale(self, scale: f32) -> Self {
|
2022-10-18 17:04:22 -06:00
|
|
|
Self {
|
|
|
|
|
font_size: self.font_size * scale,
|
|
|
|
|
line_height: self.line_height * scale,
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-02-24 11:27:45 -03:30
|
|
|
|
2023-02-04 11:13:53 +01:00
|
|
|
fn y_offset(&self) -> f32 {
|
2023-02-24 11:27:45 -03:30
|
|
|
self.font_size - self.line_height
|
|
|
|
|
}
|
2022-10-18 17:04:22 -06:00
|
|
|
}
|
|
|
|
|
|
2022-10-31 11:24:36 -06:00
|
|
|
impl fmt::Display for Metrics {
|
2022-10-18 17:04:22 -06:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(f, "{}px / {}px", self.font_size, self.line_height)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-18 17:13:48 -06:00
|
|
|
/// A buffer of text that is shaped and laid out
|
2023-03-12 10:30:03 +01:00
|
|
|
pub struct Buffer {
|
2022-11-09 10:44:51 -07:00
|
|
|
/// [BufferLine]s (or paragraphs) of text in the buffer
|
2022-11-04 09:44:54 -06:00
|
|
|
pub lines: Vec<BufferLine>,
|
2022-10-31 11:24:36 -06:00
|
|
|
metrics: Metrics,
|
2023-02-04 11:13:53 +01:00
|
|
|
width: f32,
|
|
|
|
|
height: f32,
|
2022-10-18 13:20:13 -06:00
|
|
|
scroll: i32,
|
2022-10-27 09:56:53 -06:00
|
|
|
/// True if a redraw is requires. Set to false after processing
|
2022-11-15 12:26:59 -07:00
|
|
|
redraw: bool,
|
2022-12-20 12:48:37 -07:00
|
|
|
wrap: Wrap,
|
2022-10-18 12:07:22 -06:00
|
|
|
}
|
|
|
|
|
|
2023-03-12 10:30:03 +01:00
|
|
|
impl Buffer {
|
2022-11-15 12:26:59 -07:00
|
|
|
/// Create a new [`Buffer`] with the provided [`FontSystem`] and [`Metrics`]
|
2023-02-28 19:42:53 +01:00
|
|
|
///
|
|
|
|
|
/// # Panics
|
|
|
|
|
///
|
|
|
|
|
/// Will panic if `metrics.line_height` is zero.
|
2023-03-12 10:30:20 +01:00
|
|
|
pub fn new(font_system: &mut FontSystem, metrics: Metrics) -> Self {
|
2023-02-04 11:13:53 +01:00
|
|
|
assert_ne!(metrics.line_height, 0.0, "line height cannot be 0");
|
2023-02-18 17:59:30 -08:00
|
|
|
|
2022-10-19 17:48:11 -06:00
|
|
|
let mut buffer = Self {
|
|
|
|
|
lines: Vec::new(),
|
2022-10-18 17:04:22 -06:00
|
|
|
metrics,
|
2023-02-04 11:13:53 +01:00
|
|
|
width: 0.0,
|
|
|
|
|
height: 0.0,
|
2022-10-18 17:04:22 -06:00
|
|
|
scroll: 0,
|
2022-10-18 12:07:22 -06:00
|
|
|
redraw: false,
|
2022-12-20 12:48:37 -07:00
|
|
|
wrap: Wrap::Word,
|
2022-10-19 17:48:11 -06:00
|
|
|
};
|
2023-03-12 10:30:03 +01:00
|
|
|
buffer.set_text(font_system, "", Attrs::new());
|
2022-10-19 17:48:11 -06:00
|
|
|
buffer
|
2022-10-18 12:07:22 -06:00
|
|
|
}
|
|
|
|
|
|
2023-03-12 10:30:27 +01:00
|
|
|
/// Mutably borrows the buffer together with an [`FontSystem`] for more convenient methods
|
2023-03-12 10:30:03 +01:00
|
|
|
pub fn borrow_with<'a>(
|
|
|
|
|
&'a mut self,
|
2023-03-12 10:30:09 +01:00
|
|
|
font_system: &'a mut FontSystem,
|
2023-03-12 10:30:03 +01:00
|
|
|
) -> BorrowedWithFontSystem<'a, Buffer> {
|
|
|
|
|
BorrowedWithFontSystem {
|
|
|
|
|
inner: self,
|
|
|
|
|
font_system,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-12 10:30:27 +01:00
|
|
|
fn relayout(&mut self, font_system: &mut FontSystem) {
|
2023-01-23 10:57:06 -07:00
|
|
|
#[cfg(all(feature = "std", not(target_arch = "wasm32")))]
|
2022-11-08 08:43:27 -07:00
|
|
|
let instant = std::time::Instant::now();
|
2022-10-31 11:24:36 -06:00
|
|
|
|
2022-11-15 12:26:59 -07:00
|
|
|
for line in &mut self.lines {
|
2022-10-31 11:24:36 -06:00
|
|
|
if line.shape_opt().is_some() {
|
|
|
|
|
line.reset_layout();
|
2023-03-12 10:30:03 +01:00
|
|
|
line.layout(font_system, self.metrics.font_size, self.width, self.wrap);
|
2022-10-31 11:24:36 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.redraw = true;
|
|
|
|
|
|
2023-01-23 10:57:06 -07:00
|
|
|
#[cfg(all(feature = "std", not(target_arch = "wasm32")))]
|
2022-11-08 08:43:27 -07:00
|
|
|
log::debug!("relayout: {:?}", instant.elapsed());
|
2022-10-31 11:24:36 -06:00
|
|
|
}
|
|
|
|
|
|
2023-03-12 10:30:27 +01:00
|
|
|
/// Pre-shape lines in the buffer, up to `lines`, return actual number of layout lines
|
|
|
|
|
pub fn shape_until(&mut self, font_system: &mut FontSystem, lines: i32) -> i32 {
|
2023-01-23 10:57:06 -07:00
|
|
|
#[cfg(all(feature = "std", not(target_arch = "wasm32")))]
|
2022-11-08 08:43:27 -07:00
|
|
|
let instant = std::time::Instant::now();
|
2022-10-18 12:07:22 -06:00
|
|
|
|
|
|
|
|
let mut reshaped = 0;
|
2022-10-20 19:34:56 -06:00
|
|
|
let mut total_layout = 0;
|
2022-11-15 12:26:59 -07:00
|
|
|
for line in &mut self.lines {
|
2022-10-20 19:34:56 -06:00
|
|
|
if total_layout >= lines {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-27 17:39:26 -06:00
|
|
|
if line.shape_opt().is_none() {
|
2022-10-20 19:34:56 -06:00
|
|
|
reshaped += 1;
|
|
|
|
|
}
|
2023-03-12 10:30:03 +01:00
|
|
|
let layout = line.layout(font_system, self.metrics.font_size, self.width, self.wrap);
|
2022-10-20 19:34:56 -06:00
|
|
|
total_layout += layout.len() as i32;
|
2022-10-18 12:07:22 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if reshaped > 0 {
|
2023-01-23 10:57:06 -07:00
|
|
|
#[cfg(all(feature = "std", not(target_arch = "wasm32")))]
|
2022-11-08 08:43:27 -07:00
|
|
|
log::debug!("shape_until {}: {:?}", reshaped, instant.elapsed());
|
2022-10-20 19:34:56 -06:00
|
|
|
self.redraw = true;
|
2022-10-18 12:07:22 -06:00
|
|
|
}
|
2022-10-20 19:34:56 -06:00
|
|
|
|
|
|
|
|
total_layout
|
2022-10-18 12:07:22 -06:00
|
|
|
}
|
|
|
|
|
|
2023-03-12 10:30:27 +01:00
|
|
|
/// Shape lines until cursor, also scrolling to include cursor in view
|
|
|
|
|
pub fn shape_until_cursor(&mut self, font_system: &mut FontSystem, cursor: Cursor) {
|
2023-01-23 10:57:06 -07:00
|
|
|
#[cfg(all(feature = "std", not(target_arch = "wasm32")))]
|
2022-11-08 08:43:27 -07:00
|
|
|
let instant = std::time::Instant::now();
|
2022-10-21 09:02:15 -06:00
|
|
|
|
|
|
|
|
let mut reshaped = 0;
|
|
|
|
|
let mut layout_i = 0;
|
|
|
|
|
for (line_i, line) in self.lines.iter_mut().enumerate() {
|
2022-10-31 11:24:36 -06:00
|
|
|
if line_i > cursor.line {
|
2022-10-21 09:02:15 -06:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-27 17:39:26 -06:00
|
|
|
if line.shape_opt().is_none() {
|
2022-10-21 09:02:15 -06:00
|
|
|
reshaped += 1;
|
|
|
|
|
}
|
2023-03-12 10:30:03 +01:00
|
|
|
let layout = line.layout(font_system, self.metrics.font_size, self.width, self.wrap);
|
2022-10-31 11:24:36 -06:00
|
|
|
if line_i == cursor.line {
|
|
|
|
|
let layout_cursor = self.layout_cursor(&cursor);
|
2022-10-25 20:49:15 -06:00
|
|
|
layout_i += layout_cursor.layout as i32;
|
|
|
|
|
break;
|
2022-10-21 09:02:15 -06:00
|
|
|
} else {
|
|
|
|
|
layout_i += layout.len() as i32;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if reshaped > 0 {
|
2023-01-23 10:57:06 -07:00
|
|
|
#[cfg(all(feature = "std", not(target_arch = "wasm32")))]
|
2022-11-08 08:43:27 -07:00
|
|
|
log::debug!("shape_until_cursor {}: {:?}", reshaped, instant.elapsed());
|
2022-10-21 09:02:15 -06:00
|
|
|
self.redraw = true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-31 11:24:36 -06:00
|
|
|
let lines = self.visible_lines();
|
2022-10-21 12:31:02 -06:00
|
|
|
if layout_i < self.scroll {
|
|
|
|
|
self.scroll = layout_i;
|
|
|
|
|
} else if layout_i >= self.scroll + lines {
|
|
|
|
|
self.scroll = layout_i - (lines - 1);
|
2022-10-21 09:02:15 -06:00
|
|
|
}
|
2022-10-21 12:31:02 -06:00
|
|
|
|
2023-03-12 10:30:03 +01:00
|
|
|
self.shape_until_scroll(font_system);
|
2022-10-21 09:02:15 -06:00
|
|
|
}
|
|
|
|
|
|
2023-03-12 10:30:27 +01:00
|
|
|
/// Shape lines until scroll
|
|
|
|
|
pub fn shape_until_scroll(&mut self, font_system: &mut FontSystem) {
|
2022-10-31 11:24:36 -06:00
|
|
|
let lines = self.visible_lines();
|
2022-10-18 12:42:37 -06:00
|
|
|
|
|
|
|
|
let scroll_end = self.scroll + lines;
|
2023-03-12 10:30:03 +01:00
|
|
|
let total_layout = self.shape_until(font_system, scroll_end);
|
2022-10-18 12:42:37 -06:00
|
|
|
|
2023-01-04 20:03:03 -07:00
|
|
|
self.scroll = cmp::max(0, cmp::min(total_layout - (lines - 1), self.scroll));
|
2022-10-18 12:42:37 -06:00
|
|
|
}
|
|
|
|
|
|
2022-10-31 11:24:36 -06:00
|
|
|
pub fn layout_cursor(&self, cursor: &Cursor) -> LayoutCursor {
|
2022-10-26 12:23:03 -06:00
|
|
|
let line = &self.lines[cursor.line];
|
2022-10-22 11:42:08 -06:00
|
|
|
|
2022-11-15 12:26:59 -07:00
|
|
|
//TODO: ensure layout is done?
|
|
|
|
|
let layout = line.layout_opt().as_ref().expect("layout not found");
|
2022-10-22 11:42:08 -06:00
|
|
|
for (layout_i, layout_line) in layout.iter().enumerate() {
|
|
|
|
|
for (glyph_i, glyph) in layout_line.glyphs.iter().enumerate() {
|
2023-01-14 10:25:13 -07:00
|
|
|
let cursor_end =
|
|
|
|
|
Cursor::new_with_affinity(cursor.line, glyph.end, Affinity::Before);
|
|
|
|
|
let cursor_start =
|
|
|
|
|
Cursor::new_with_affinity(cursor.line, glyph.start, Affinity::After);
|
2023-01-10 20:12:12 -07:00
|
|
|
let (cursor_left, cursor_right) = if glyph.level.is_ltr() {
|
|
|
|
|
(cursor_start, cursor_end)
|
|
|
|
|
} else {
|
|
|
|
|
(cursor_end, cursor_start)
|
|
|
|
|
};
|
|
|
|
|
if *cursor == cursor_left {
|
2023-01-04 20:03:03 -07:00
|
|
|
return LayoutCursor::new(cursor.line, layout_i, glyph_i);
|
2022-10-22 11:42:08 -06:00
|
|
|
}
|
2023-01-10 20:12:12 -07:00
|
|
|
if *cursor == cursor_right {
|
|
|
|
|
return LayoutCursor::new(cursor.line, layout_i, glyph_i + 1);
|
2022-10-22 11:42:08 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fall back to start of line
|
|
|
|
|
//TODO: should this be the end of the line?
|
2023-01-04 20:03:03 -07:00
|
|
|
LayoutCursor::new(cursor.line, 0, 0)
|
2022-10-22 11:42:08 -06:00
|
|
|
}
|
|
|
|
|
|
2023-03-12 10:30:27 +01:00
|
|
|
/// Shape the provided line index and return the result
|
|
|
|
|
pub fn line_shape(
|
2023-03-12 10:30:03 +01:00
|
|
|
&mut self,
|
2023-03-12 10:30:20 +01:00
|
|
|
font_system: &mut FontSystem,
|
2023-03-12 10:30:03 +01:00
|
|
|
line_i: usize,
|
|
|
|
|
) -> Option<&ShapeLine> {
|
2022-10-31 11:24:36 -06:00
|
|
|
let line = self.lines.get_mut(line_i)?;
|
2023-03-12 10:30:03 +01:00
|
|
|
Some(line.shape(font_system))
|
2022-10-22 11:42:08 -06:00
|
|
|
}
|
|
|
|
|
|
2023-03-12 10:30:27 +01:00
|
|
|
/// Lay out the provided line index and return the result
|
|
|
|
|
pub fn line_layout(
|
2023-03-12 10:30:03 +01:00
|
|
|
&mut self,
|
2023-03-12 10:30:20 +01:00
|
|
|
font_system: &mut FontSystem,
|
2023-03-12 10:30:03 +01:00
|
|
|
line_i: usize,
|
|
|
|
|
) -> Option<&[LayoutLine]> {
|
2022-10-31 11:24:36 -06:00
|
|
|
let line = self.lines.get_mut(line_i)?;
|
2023-03-12 10:30:03 +01:00
|
|
|
Some(line.layout(font_system, self.metrics.font_size, self.width, self.wrap))
|
2022-10-19 08:34:34 -06:00
|
|
|
}
|
|
|
|
|
|
2022-11-15 12:26:59 -07:00
|
|
|
/// Get the current [`Metrics`]
|
2022-10-31 11:24:36 -06:00
|
|
|
pub fn metrics(&self) -> Metrics {
|
2022-10-18 17:04:22 -06:00
|
|
|
self.metrics
|
2022-10-18 12:07:22 -06:00
|
|
|
}
|
|
|
|
|
|
2023-03-12 10:30:27 +01:00
|
|
|
/// Set the current [`Metrics`]
|
|
|
|
|
///
|
|
|
|
|
/// # Panics
|
|
|
|
|
///
|
|
|
|
|
/// Will panic if `metrics.font_size` is zero.
|
|
|
|
|
pub fn set_metrics(&mut self, font_system: &mut FontSystem, metrics: Metrics) {
|
2022-10-18 17:04:22 -06:00
|
|
|
if metrics != self.metrics {
|
2023-02-04 11:13:53 +01:00
|
|
|
assert_ne!(metrics.font_size, 0.0, "font size cannot be 0");
|
2022-10-18 17:04:22 -06:00
|
|
|
self.metrics = metrics;
|
2023-03-12 10:30:03 +01:00
|
|
|
self.relayout(font_system);
|
|
|
|
|
self.shape_until_scroll(font_system);
|
2022-10-18 12:42:37 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-20 12:48:37 -07:00
|
|
|
/// Get the current [`Wrap`]
|
|
|
|
|
pub fn wrap(&self) -> Wrap {
|
|
|
|
|
self.wrap
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-12 10:30:27 +01:00
|
|
|
/// Set the current [`Wrap`]
|
|
|
|
|
pub fn set_wrap(&mut self, font_system: &mut FontSystem, wrap: Wrap) {
|
2022-12-20 12:48:37 -07:00
|
|
|
if wrap != self.wrap {
|
|
|
|
|
self.wrap = wrap;
|
2023-03-12 10:30:03 +01:00
|
|
|
self.relayout(font_system);
|
|
|
|
|
self.shape_until_scroll(font_system);
|
2022-12-20 12:48:37 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-18 17:04:22 -06:00
|
|
|
/// Get the current buffer dimensions (width, height)
|
2023-02-04 11:13:53 +01:00
|
|
|
pub fn size(&self) -> (f32, f32) {
|
2022-10-18 17:04:22 -06:00
|
|
|
(self.width, self.height)
|
2022-10-18 13:05:36 -06:00
|
|
|
}
|
|
|
|
|
|
2023-03-12 10:30:27 +01:00
|
|
|
/// Set the current buffer dimensions
|
|
|
|
|
pub fn set_size(&mut self, font_system: &mut FontSystem, width: f32, height: f32) {
|
2023-02-04 11:13:53 +01:00
|
|
|
let clamped_width = width.max(0.0);
|
|
|
|
|
let clamped_height = height.max(0.0);
|
2023-01-26 22:16:23 -03:30
|
|
|
|
|
|
|
|
if clamped_width != self.width || clamped_height != self.height {
|
|
|
|
|
self.width = clamped_width;
|
|
|
|
|
self.height = clamped_height;
|
2023-03-12 10:30:03 +01:00
|
|
|
self.relayout(font_system);
|
|
|
|
|
self.shape_until_scroll(font_system);
|
2022-10-18 12:42:37 -06:00
|
|
|
}
|
2022-10-18 12:07:22 -06:00
|
|
|
}
|
|
|
|
|
|
2022-10-18 17:04:22 -06:00
|
|
|
/// Get the current scroll location
|
|
|
|
|
pub fn scroll(&self) -> i32 {
|
|
|
|
|
self.scroll
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-31 11:36:54 -06:00
|
|
|
/// Set the current scroll location
|
2022-10-31 11:24:36 -06:00
|
|
|
pub fn set_scroll(&mut self, scroll: i32) {
|
|
|
|
|
if scroll != self.scroll {
|
|
|
|
|
self.scroll = scroll;
|
|
|
|
|
self.redraw = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-18 17:04:22 -06:00
|
|
|
/// Get the number of lines that can be viewed in the buffer
|
2022-10-31 11:24:36 -06:00
|
|
|
pub fn visible_lines(&self) -> i32 {
|
2023-02-04 11:13:53 +01:00
|
|
|
(self.height / self.metrics.line_height) as i32
|
2022-10-18 17:04:22 -06:00
|
|
|
}
|
|
|
|
|
|
2023-03-12 10:30:27 +01:00
|
|
|
/// Set text of buffer, using provided attributes for each line by default
|
|
|
|
|
pub fn set_text(&mut self, font_system: &mut FontSystem, text: &str, attrs: Attrs) {
|
2022-10-19 17:48:11 -06:00
|
|
|
self.lines.clear();
|
|
|
|
|
for line in text.lines() {
|
2023-01-04 20:03:03 -07:00
|
|
|
self.lines
|
|
|
|
|
.push(BufferLine::new(line.to_string(), AttrsList::new(attrs)));
|
2022-10-19 17:48:11 -06:00
|
|
|
}
|
|
|
|
|
// Make sure there is always one line
|
|
|
|
|
if self.lines.is_empty() {
|
2023-01-04 20:03:03 -07:00
|
|
|
self.lines
|
|
|
|
|
.push(BufferLine::new(String::new(), AttrsList::new(attrs)));
|
2022-10-19 13:15:07 -06:00
|
|
|
}
|
2022-10-20 19:34:56 -06:00
|
|
|
|
2022-10-19 13:15:07 -06:00
|
|
|
self.scroll = 0;
|
2022-10-20 19:34:56 -06:00
|
|
|
|
2023-03-12 10:30:03 +01:00
|
|
|
self.shape_until_scroll(font_system);
|
2022-10-19 13:15:07 -06:00
|
|
|
}
|
|
|
|
|
|
2022-11-15 12:26:59 -07:00
|
|
|
/// True if a redraw is needed
|
|
|
|
|
pub fn redraw(&self) -> bool {
|
|
|
|
|
self.redraw
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Set redraw needed flag
|
|
|
|
|
pub fn set_redraw(&mut self, redraw: bool) {
|
|
|
|
|
self.redraw = redraw;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-31 11:24:36 -06:00
|
|
|
/// Get the visible layout runs for rendering and other tasks
|
2023-03-12 10:30:03 +01:00
|
|
|
pub fn layout_runs(&self) -> LayoutRunIter {
|
2022-10-31 11:24:36 -06:00
|
|
|
LayoutRunIter::new(self)
|
2022-10-18 12:07:22 -06:00
|
|
|
}
|
2022-10-19 07:36:27 -06:00
|
|
|
|
2022-10-31 11:24:36 -06:00
|
|
|
/// Convert x, y position to Cursor (hit detection)
|
2023-02-04 11:13:53 +01:00
|
|
|
pub fn hit(&self, x: f32, y: f32) -> Option<Cursor> {
|
2023-01-23 10:57:06 -07:00
|
|
|
#[cfg(all(feature = "std", not(target_arch = "wasm32")))]
|
2022-11-08 08:43:27 -07:00
|
|
|
let instant = std::time::Instant::now();
|
2022-10-19 07:36:27 -06:00
|
|
|
|
|
|
|
|
let font_size = self.metrics.font_size;
|
|
|
|
|
let line_height = self.metrics.line_height;
|
|
|
|
|
|
2022-10-25 20:27:46 -06:00
|
|
|
let mut new_cursor_opt = None;
|
2022-10-25 13:13:13 -06:00
|
|
|
|
2022-11-05 17:28:44 -06:00
|
|
|
let mut runs = self.layout_runs().peekable();
|
2022-11-10 11:55:12 -07:00
|
|
|
let mut first_run = true;
|
2022-11-05 17:28:44 -06:00
|
|
|
while let Some(run) = runs.next() {
|
2022-10-25 13:13:13 -06:00
|
|
|
let line_y = run.line_y;
|
|
|
|
|
|
2022-11-10 11:55:12 -07:00
|
|
|
if first_run && y < line_y - font_size {
|
|
|
|
|
first_run = false;
|
|
|
|
|
let new_cursor = Cursor::new(run.line_i, 0);
|
|
|
|
|
new_cursor_opt = Some(new_cursor);
|
2023-01-04 20:03:03 -07:00
|
|
|
} else if y >= line_y - font_size && y < line_y - font_size + line_height {
|
2022-10-25 13:13:13 -06:00
|
|
|
let mut new_cursor_glyph = run.glyphs.len();
|
|
|
|
|
let mut new_cursor_char = 0;
|
2023-01-14 10:25:13 -07:00
|
|
|
let mut new_cursor_affinity = Affinity::After;
|
2022-11-10 11:55:12 -07:00
|
|
|
|
|
|
|
|
let mut first_glyph = true;
|
2022-11-15 12:26:59 -07:00
|
|
|
|
2022-10-25 13:13:13 -06:00
|
|
|
'hit: for (glyph_i, glyph) in run.glyphs.iter().enumerate() {
|
2022-11-15 12:26:59 -07:00
|
|
|
if first_glyph {
|
2022-11-10 11:55:12 -07:00
|
|
|
first_glyph = false;
|
2023-02-04 11:13:53 +01:00
|
|
|
if (run.rtl && x > glyph.x) || (!run.rtl && x < 0.0) {
|
2022-11-10 11:55:12 -07:00
|
|
|
new_cursor_glyph = 0;
|
|
|
|
|
new_cursor_char = 0;
|
|
|
|
|
}
|
2022-11-15 12:26:59 -07:00
|
|
|
}
|
2023-02-04 11:13:53 +01:00
|
|
|
if x >= glyph.x && x <= glyph.x + glyph.w {
|
2022-10-25 13:13:13 -06:00
|
|
|
new_cursor_glyph = glyph_i;
|
|
|
|
|
|
|
|
|
|
let cluster = &run.text[glyph.start..glyph.end];
|
|
|
|
|
let total = cluster.grapheme_indices(true).count();
|
|
|
|
|
let mut egc_x = glyph.x;
|
|
|
|
|
let egc_w = glyph.w / (total as f32);
|
|
|
|
|
for (egc_i, egc) in cluster.grapheme_indices(true) {
|
2023-02-04 11:13:53 +01:00
|
|
|
if x >= egc_x && x <= egc_x + egc_w {
|
2022-10-25 13:13:13 -06:00
|
|
|
new_cursor_char = egc_i;
|
|
|
|
|
|
2023-02-04 11:13:53 +01:00
|
|
|
let right_half = x >= egc_x + egc_w / 2.0;
|
2022-12-16 16:49:29 -07:00
|
|
|
if right_half != glyph.level.is_rtl() {
|
2022-10-25 13:13:13 -06:00
|
|
|
// If clicking on last half of glyph, move cursor past glyph
|
|
|
|
|
new_cursor_char += egc.len();
|
2023-01-14 10:25:13 -07:00
|
|
|
new_cursor_affinity = Affinity::Before;
|
2022-10-24 14:18:40 -06:00
|
|
|
}
|
2022-10-25 13:13:13 -06:00
|
|
|
break 'hit;
|
2022-10-24 14:18:40 -06:00
|
|
|
}
|
2022-10-25 13:13:13 -06:00
|
|
|
egc_x += egc_w;
|
|
|
|
|
}
|
2022-10-24 14:18:40 -06:00
|
|
|
|
2023-02-04 11:13:53 +01:00
|
|
|
let right_half = x >= glyph.x + glyph.w / 2.0;
|
2022-12-16 16:49:29 -07:00
|
|
|
if right_half != glyph.level.is_rtl() {
|
2022-10-25 13:13:13 -06:00
|
|
|
// If clicking on last half of glyph, move cursor past glyph
|
|
|
|
|
new_cursor_char = cluster.len();
|
2023-01-14 10:25:13 -07:00
|
|
|
new_cursor_affinity = Affinity::Before;
|
2022-10-19 09:31:01 -06:00
|
|
|
}
|
2022-10-25 13:13:13 -06:00
|
|
|
break 'hit;
|
2022-10-19 07:36:27 -06:00
|
|
|
}
|
2022-10-25 13:13:13 -06:00
|
|
|
}
|
2022-10-21 09:02:15 -06:00
|
|
|
|
2022-10-31 11:24:36 -06:00
|
|
|
let mut new_cursor = Cursor::new(run.line_i, 0);
|
2022-10-25 13:13:13 -06:00
|
|
|
|
|
|
|
|
match run.glyphs.get(new_cursor_glyph) {
|
|
|
|
|
Some(glyph) => {
|
|
|
|
|
// Position at glyph
|
|
|
|
|
new_cursor.index = glyph.start + new_cursor_char;
|
2023-01-10 20:12:12 -07:00
|
|
|
new_cursor.affinity = new_cursor_affinity;
|
2023-01-04 20:03:03 -07:00
|
|
|
}
|
|
|
|
|
None => {
|
|
|
|
|
if let Some(glyph) = run.glyphs.last() {
|
|
|
|
|
// Position at end of line
|
|
|
|
|
new_cursor.index = glyph.end;
|
2023-01-14 10:25:13 -07:00
|
|
|
new_cursor.affinity = Affinity::Before;
|
2023-01-04 20:03:03 -07:00
|
|
|
}
|
|
|
|
|
}
|
2022-10-25 13:13:13 -06:00
|
|
|
}
|
2022-10-19 07:36:27 -06:00
|
|
|
|
2022-10-25 20:27:46 -06:00
|
|
|
new_cursor_opt = Some(new_cursor);
|
|
|
|
|
|
2022-10-25 13:13:13 -06:00
|
|
|
break;
|
2022-11-05 17:28:44 -06:00
|
|
|
} else if runs.peek().is_none() && y > run.line_y {
|
2022-11-04 13:57:32 -06:00
|
|
|
let mut new_cursor = Cursor::new(run.line_i, 0);
|
|
|
|
|
if let Some(glyph) = run.glyphs.last() {
|
2023-01-10 20:12:12 -07:00
|
|
|
new_cursor = run.cursor_from_glyph_right(glyph);
|
2022-11-04 13:57:32 -06:00
|
|
|
}
|
|
|
|
|
new_cursor_opt = Some(new_cursor);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-10-19 07:36:27 -06:00
|
|
|
|
2023-01-23 10:57:06 -07:00
|
|
|
#[cfg(all(feature = "std", not(target_arch = "wasm32")))]
|
2022-11-08 08:43:27 -07:00
|
|
|
log::trace!("click({}, {}): {:?}", x, y, instant.elapsed());
|
2022-10-25 20:27:46 -06:00
|
|
|
|
|
|
|
|
new_cursor_opt
|
2022-10-19 07:36:27 -06:00
|
|
|
}
|
|
|
|
|
|
2023-03-12 10:30:27 +01:00
|
|
|
/// Draw the buffer
|
2022-10-25 11:40:10 -06:00
|
|
|
#[cfg(feature = "swash")]
|
2023-03-12 10:30:27 +01:00
|
|
|
pub fn draw<F>(
|
2023-03-12 10:30:03 +01:00
|
|
|
&self,
|
2023-03-12 10:30:20 +01:00
|
|
|
font_system: &mut FontSystem,
|
2023-03-12 10:30:03 +01:00
|
|
|
cache: &mut crate::SwashCache,
|
|
|
|
|
color: Color,
|
|
|
|
|
mut f: F,
|
|
|
|
|
) where
|
2023-01-04 20:03:03 -07:00
|
|
|
F: FnMut(i32, i32, u32, u32, Color),
|
2022-10-25 11:40:10 -06:00
|
|
|
{
|
2022-10-25 12:52:46 -06:00
|
|
|
for run in self.layout_runs() {
|
|
|
|
|
for glyph in run.glyphs.iter() {
|
2022-10-25 11:40:10 -06:00
|
|
|
let (cache_key, x_int, y_int) = (glyph.cache_key, glyph.x_int, glyph.y_int);
|
2022-10-26 14:16:48 -06:00
|
|
|
|
|
|
|
|
let glyph_color = match glyph.color_opt {
|
2022-10-27 09:07:47 -06:00
|
|
|
Some(some) => some,
|
2022-10-26 14:16:48 -06:00
|
|
|
None => color,
|
|
|
|
|
};
|
|
|
|
|
|
2023-03-12 10:30:03 +01:00
|
|
|
cache.with_pixels(font_system, cache_key, glyph_color, |x, y, color| {
|
2023-02-04 11:13:53 +01:00
|
|
|
f(x_int + x, run.line_y as i32 + y_int + y, 1, 1, color);
|
2022-10-25 11:40:10 -06:00
|
|
|
});
|
2022-10-20 19:34:56 -06:00
|
|
|
}
|
2022-10-25 12:52:46 -06:00
|
|
|
}
|
2022-10-19 07:36:27 -06:00
|
|
|
}
|
2022-10-18 12:07:22 -06:00
|
|
|
}
|
2023-03-12 10:30:03 +01:00
|
|
|
|
|
|
|
|
impl<'a> BorrowedWithFontSystem<'a, Buffer> {
|
|
|
|
|
/// Pre-shape lines in the buffer, up to `lines`, return actual number of layout lines
|
|
|
|
|
pub fn shape_until(&mut self, lines: i32) -> i32 {
|
|
|
|
|
self.inner.shape_until(self.font_system, lines)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Shape lines until cursor, also scrolling to include cursor in view
|
|
|
|
|
pub fn shape_until_cursor(&mut self, cursor: Cursor) {
|
|
|
|
|
self.inner.shape_until_cursor(self.font_system, cursor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Shape lines until scroll
|
|
|
|
|
pub fn shape_until_scroll(&mut self) {
|
|
|
|
|
self.inner.shape_until_scroll(self.font_system);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Shape the provided line index and return the result
|
|
|
|
|
pub fn line_shape(&mut self, line_i: usize) -> Option<&ShapeLine> {
|
|
|
|
|
self.inner.line_shape(self.font_system, line_i)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Lay out the provided line index and return the result
|
|
|
|
|
pub fn line_layout(&mut self, line_i: usize) -> Option<&[LayoutLine]> {
|
|
|
|
|
self.inner.line_layout(self.font_system, line_i)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Set the current [`Metrics`]
|
|
|
|
|
///
|
|
|
|
|
/// # Panics
|
|
|
|
|
///
|
|
|
|
|
/// Will panic if `metrics.font_size` is zero.
|
|
|
|
|
pub fn set_metrics(&mut self, metrics: Metrics) {
|
|
|
|
|
self.inner.set_metrics(self.font_system, metrics);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Set the current [`Wrap`]
|
|
|
|
|
pub fn set_wrap(&mut self, wrap: Wrap) {
|
|
|
|
|
self.inner.set_wrap(self.font_system, wrap);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Set the current buffer dimensions
|
|
|
|
|
pub fn set_size(&mut self, width: f32, height: f32) {
|
|
|
|
|
self.inner.set_size(self.font_system, width, height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Set text of buffer, using provided attributes for each line by default
|
|
|
|
|
pub fn set_text(&mut self, text: &str, attrs: Attrs) {
|
|
|
|
|
self.inner.set_text(self.font_system, text, attrs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Draw the buffer
|
|
|
|
|
#[cfg(feature = "swash")]
|
2023-03-12 10:30:20 +01:00
|
|
|
pub fn draw<F>(&mut self, cache: &mut crate::SwashCache, color: Color, f: F)
|
2023-03-12 10:30:03 +01:00
|
|
|
where
|
|
|
|
|
F: FnMut(i32, i32, u32, u32, Color),
|
|
|
|
|
{
|
|
|
|
|
self.inner.draw(self.font_system, cache, color, f);
|
|
|
|
|
}
|
|
|
|
|
}
|