refactor: address clippy warnings and improve code quality (#409)
- Fix string formatting with modern interpolation syntax - Improve Debug implementation with finish_non_exhaustive() - Fix function placement in shape.rs to avoid items_after_statements warning - Use more idiomatic Rust patterns (map_or_else, next_back) - Clean up conditional imports in vi.rs - Convert multiple methods to `const` functions for optimization and consistency - Introduce `core_maths` for enhanced no-std compatibility - Update `Cargo.toml` for the new optional dependency and feature adjustments
This commit is contained in:
parent
e80dbc3607
commit
a2f1f4b2a0
20 changed files with 282 additions and 299 deletions
|
|
@ -11,6 +11,7 @@ rust-version = "1.75"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "2.4.1"
|
bitflags = "2.4.1"
|
||||||
|
core_maths = { version = "0.1.1", optional = true }
|
||||||
cosmic_undo_2 = { version = "0.2.0", optional = true }
|
cosmic_undo_2 = { version = "0.2.0", optional = true }
|
||||||
fontdb = { version = "0.23", default-features = false }
|
fontdb = { version = "0.23", default-features = false }
|
||||||
hashbrown = { version = "0.14.1", optional = true, default-features = false }
|
hashbrown = { version = "0.14.1", optional = true, default-features = false }
|
||||||
|
|
@ -48,7 +49,7 @@ optional = true
|
||||||
default = ["std", "swash", "fontconfig"]
|
default = ["std", "swash", "fontconfig"]
|
||||||
fontconfig = ["fontdb/fontconfig", "std"]
|
fontconfig = ["fontdb/fontconfig", "std"]
|
||||||
monospace_fallback = []
|
monospace_fallback = []
|
||||||
no_std = ["rustybuzz/libm", "hashbrown", "dep:libm"]
|
no_std = ["rustybuzz/libm", "hashbrown", "dep:libm", "core_maths"]
|
||||||
peniko = ["dep:peniko"]
|
peniko = ["dep:peniko"]
|
||||||
shape-run-cache = []
|
shape-run-cache = []
|
||||||
std = [
|
std = [
|
||||||
|
|
|
||||||
62
src/attrs.rs
62
src/attrs.rs
|
|
@ -30,37 +30,37 @@ impl Color {
|
||||||
|
|
||||||
/// Get a tuple over all of the attributes, in `(r, g, b, a)` order.
|
/// Get a tuple over all of the attributes, in `(r, g, b, a)` order.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_rgba_tuple(self) -> (u8, u8, u8, u8) {
|
pub const fn as_rgba_tuple(self) -> (u8, u8, u8, u8) {
|
||||||
(self.r(), self.g(), self.b(), self.a())
|
(self.r(), self.g(), self.b(), self.a())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an array over all of the components, in `[r, g, b, a]` order.
|
/// Get an array over all of the components, in `[r, g, b, a]` order.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_rgba(self) -> [u8; 4] {
|
pub const fn as_rgba(self) -> [u8; 4] {
|
||||||
[self.r(), self.g(), self.b(), self.a()]
|
[self.r(), self.g(), self.b(), self.a()]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the red component
|
/// Get the red component
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn r(&self) -> u8 {
|
pub const fn r(&self) -> u8 {
|
||||||
((self.0 & 0x00_FF_00_00) >> 16) as u8
|
((self.0 & 0x00_FF_00_00) >> 16) as u8
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the green component
|
/// Get the green component
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn g(&self) -> u8 {
|
pub const fn g(&self) -> u8 {
|
||||||
((self.0 & 0x00_00_FF_00) >> 8) as u8
|
((self.0 & 0x00_00_FF_00) >> 8) as u8
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the blue component
|
/// Get the blue component
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn b(&self) -> u8 {
|
pub const fn b(&self) -> u8 {
|
||||||
(self.0 & 0x00_00_00_FF) as u8
|
(self.0 & 0x00_00_00_FF) as u8
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the alpha component
|
/// Get the alpha component
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn a(&self) -> u8 {
|
pub const fn a(&self) -> u8 {
|
||||||
((self.0 & 0xFF_00_00_00) >> 24) as u8
|
((self.0 & 0xFF_00_00_00) >> 24) as u8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -79,23 +79,23 @@ pub enum FamilyOwned {
|
||||||
impl FamilyOwned {
|
impl FamilyOwned {
|
||||||
pub fn new(family: Family) -> Self {
|
pub fn new(family: Family) -> Self {
|
||||||
match family {
|
match family {
|
||||||
Family::Name(name) => FamilyOwned::Name(SmolStr::from(name)),
|
Family::Name(name) => Self::Name(SmolStr::from(name)),
|
||||||
Family::Serif => FamilyOwned::Serif,
|
Family::Serif => Self::Serif,
|
||||||
Family::SansSerif => FamilyOwned::SansSerif,
|
Family::SansSerif => Self::SansSerif,
|
||||||
Family::Cursive => FamilyOwned::Cursive,
|
Family::Cursive => Self::Cursive,
|
||||||
Family::Fantasy => FamilyOwned::Fantasy,
|
Family::Fantasy => Self::Fantasy,
|
||||||
Family::Monospace => FamilyOwned::Monospace,
|
Family::Monospace => Self::Monospace,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_family(&self) -> Family {
|
pub fn as_family(&self) -> Family {
|
||||||
match self {
|
match self {
|
||||||
FamilyOwned::Name(name) => Family::Name(name),
|
Self::Name(name) => Family::Name(name),
|
||||||
FamilyOwned::Serif => Family::Serif,
|
Self::Serif => Family::Serif,
|
||||||
FamilyOwned::SansSerif => Family::SansSerif,
|
Self::SansSerif => Family::SansSerif,
|
||||||
FamilyOwned::Cursive => Family::Cursive,
|
Self::Cursive => Family::Cursive,
|
||||||
FamilyOwned::Fantasy => Family::Fantasy,
|
Self::Fantasy => Family::Fantasy,
|
||||||
FamilyOwned::Monospace => Family::Monospace,
|
Self::Monospace => Family::Monospace,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -153,7 +153,7 @@ impl FeatureTag {
|
||||||
/// Stylistic Set 2 (font-specific alternate glyphs)
|
/// Stylistic Set 2 (font-specific alternate glyphs)
|
||||||
pub const STYLISTIC_SET_2: Self = Self::new(b"ss02");
|
pub const STYLISTIC_SET_2: Self = Self::new(b"ss02");
|
||||||
|
|
||||||
pub fn as_bytes(&self) -> &[u8; 4] {
|
pub const fn as_bytes(&self) -> &[u8; 4] {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -170,7 +170,7 @@ pub struct FontFeatures {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontFeatures {
|
impl FontFeatures {
|
||||||
pub fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
features: Vec::new(),
|
features: Vec::new(),
|
||||||
}
|
}
|
||||||
|
|
@ -244,7 +244,7 @@ impl<'a> Attrs<'a> {
|
||||||
/// Create a new set of attributes with sane defaults
|
/// Create a new set of attributes with sane defaults
|
||||||
///
|
///
|
||||||
/// This defaults to a regular Sans-Serif font.
|
/// This defaults to a regular Sans-Serif font.
|
||||||
pub fn new() -> Self {
|
pub const fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
color_opt: None,
|
color_opt: None,
|
||||||
family: Family::SansSerif,
|
family: Family::SansSerif,
|
||||||
|
|
@ -260,43 +260,43 @@ impl<'a> Attrs<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set [Color]
|
/// Set [Color]
|
||||||
pub fn color(mut self, color: Color) -> Self {
|
pub const fn color(mut self, color: Color) -> Self {
|
||||||
self.color_opt = Some(color);
|
self.color_opt = Some(color);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set [Family]
|
/// Set [Family]
|
||||||
pub fn family(mut self, family: Family<'a>) -> Self {
|
pub const fn family(mut self, family: Family<'a>) -> Self {
|
||||||
self.family = family;
|
self.family = family;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set [Stretch]
|
/// Set [Stretch]
|
||||||
pub fn stretch(mut self, stretch: Stretch) -> Self {
|
pub const fn stretch(mut self, stretch: Stretch) -> Self {
|
||||||
self.stretch = stretch;
|
self.stretch = stretch;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set [Style]
|
/// Set [Style]
|
||||||
pub fn style(mut self, style: Style) -> Self {
|
pub const fn style(mut self, style: Style) -> Self {
|
||||||
self.style = style;
|
self.style = style;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set [Weight]
|
/// Set [Weight]
|
||||||
pub fn weight(mut self, weight: Weight) -> Self {
|
pub const fn weight(mut self, weight: Weight) -> Self {
|
||||||
self.weight = weight;
|
self.weight = weight;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set metadata
|
/// Set metadata
|
||||||
pub fn metadata(mut self, metadata: usize) -> Self {
|
pub const fn metadata(mut self, metadata: usize) -> Self {
|
||||||
self.metadata = metadata;
|
self.metadata = metadata;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set [`CacheKeyFlags`]
|
/// Set [`CacheKeyFlags`]
|
||||||
pub fn cache_key_flags(mut self, cache_key_flags: CacheKeyFlags) -> Self {
|
pub const fn cache_key_flags(mut self, cache_key_flags: CacheKeyFlags) -> Self {
|
||||||
self.cache_key_flags = cache_key_flags;
|
self.cache_key_flags = cache_key_flags;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -308,7 +308,7 @@ impl<'a> Attrs<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set letter spacing (tracking) in EM
|
/// Set letter spacing (tracking) in EM
|
||||||
pub fn letter_spacing(mut self, letter_spacing: f32) -> Self {
|
pub const fn letter_spacing(mut self, letter_spacing: f32) -> Self {
|
||||||
self.letter_spacing_opt = Some(LetterSpacing(letter_spacing));
|
self.letter_spacing_opt = Some(LetterSpacing(letter_spacing));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
@ -471,7 +471,9 @@ impl AttrsList {
|
||||||
for span in self.spans.iter() {
|
for span in self.spans.iter() {
|
||||||
if span.0.end <= index {
|
if span.0.end <= index {
|
||||||
continue;
|
continue;
|
||||||
} else if span.0.start >= index {
|
}
|
||||||
|
|
||||||
|
if span.0.start >= index {
|
||||||
removes.push((span.0.clone(), false));
|
removes.push((span.0.clone(), false));
|
||||||
} else {
|
} else {
|
||||||
removes.push((span.0.clone(), true));
|
removes.push((span.0.clone(), true));
|
||||||
|
|
|
||||||
|
|
@ -63,13 +63,12 @@ impl<'text> Iterator for BidiParagraphs<'text> {
|
||||||
let paragraph = &self.text[para.range];
|
let paragraph = &self.text[para.range];
|
||||||
// `para.range` includes the newline that splits the line, so remove it if present
|
// `para.range` includes the newline that splits the line, so remove it if present
|
||||||
let mut char_indices = paragraph.char_indices();
|
let mut char_indices = paragraph.char_indices();
|
||||||
if let Some(i) = char_indices.next_back().and_then(|(i, c)| {
|
char_indices
|
||||||
// `BidiClass::B` is a Paragraph_Separator (various newline characters)
|
.next_back()
|
||||||
(bidi_class(c) == BidiClass::B).then_some(i)
|
.and_then(|(i, c)| {
|
||||||
}) {
|
// `BidiClass::B` is a Paragraph_Separator (various newline characters)
|
||||||
Some(¶graph[0..i])
|
(bidi_class(c) == BidiClass::B).then_some(i)
|
||||||
} else {
|
})
|
||||||
Some(paragraph)
|
.map_or(Some(paragraph), |i| Some(¶graph[0..i]))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
111
src/buffer.rs
111
src/buffer.rs
|
|
@ -2,11 +2,16 @@
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
use alloc::{string::String, vec::Vec};
|
use alloc::{string::String, vec::Vec};
|
||||||
|
|
||||||
use core::{cmp, fmt};
|
use core::{cmp, fmt};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
use core_maths::CoreFloat;
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
#[cfg(feature = "swash")]
|
#[cfg(feature = "swash")]
|
||||||
use crate::Color;
|
use crate::Color;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Affinity, Align, Attrs, AttrsList, BidiParagraphs, BorrowedWithFontSystem, BufferLine, Cursor,
|
Affinity, Align, Attrs, AttrsList, BidiParagraphs, BorrowedWithFontSystem, BufferLine, Cursor,
|
||||||
FontSystem, LayoutCursor, LayoutGlyph, LayoutLine, LineEnding, LineIter, Motion, Scroll,
|
FontSystem, LayoutCursor, LayoutGlyph, LayoutLine, LineEnding, LineIter, Motion, Scroll,
|
||||||
|
|
@ -45,36 +50,34 @@ impl LayoutRun<'_> {
|
||||||
let mut x_end = None;
|
let mut x_end = None;
|
||||||
let rtl_factor = if self.rtl { 1. } else { 0. };
|
let rtl_factor = if self.rtl { 1. } else { 0. };
|
||||||
let ltr_factor = 1. - rtl_factor;
|
let ltr_factor = 1. - rtl_factor;
|
||||||
for glyph in self.glyphs.iter() {
|
for glyph in self.glyphs {
|
||||||
let cursor = self.cursor_from_glyph_left(glyph);
|
let cursor = self.cursor_from_glyph_left(glyph);
|
||||||
if cursor >= cursor_start && cursor <= cursor_end {
|
if cursor >= cursor_start && cursor <= cursor_end {
|
||||||
if x_start.is_none() {
|
if x_start.is_none() {
|
||||||
x_start = Some(glyph.x + glyph.w * rtl_factor);
|
x_start = Some(glyph.x + glyph.w.mul_add(rtl_factor, 0.0));
|
||||||
}
|
}
|
||||||
x_end = Some(glyph.x + glyph.w * rtl_factor);
|
x_end = Some(glyph.x + glyph.w.mul_add(rtl_factor, 0.0));
|
||||||
}
|
}
|
||||||
let cursor = self.cursor_from_glyph_right(glyph);
|
let cursor = self.cursor_from_glyph_right(glyph);
|
||||||
if cursor >= cursor_start && cursor <= cursor_end {
|
if cursor >= cursor_start && cursor <= cursor_end {
|
||||||
if x_start.is_none() {
|
if x_start.is_none() {
|
||||||
x_start = Some(glyph.x + glyph.w * ltr_factor);
|
x_start = Some(glyph.x + glyph.w.mul_add(ltr_factor, 0.0));
|
||||||
}
|
}
|
||||||
x_end = Some(glyph.x + glyph.w * ltr_factor);
|
x_end = Some(glyph.x + glyph.w.mul_add(ltr_factor, 0.0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(x_start) = x_start {
|
x_start.map(|x_start| {
|
||||||
let x_end = x_end.expect("end of cursor not found");
|
let x_end = x_end.expect("end of cursor not found");
|
||||||
let (x_start, x_end) = if x_start < x_end {
|
let (x_start, x_end) = if x_start < x_end {
|
||||||
(x_start, x_end)
|
(x_start, x_end)
|
||||||
} else {
|
} else {
|
||||||
(x_end, x_start)
|
(x_end, x_start)
|
||||||
};
|
};
|
||||||
Some((x_start, x_end - x_start))
|
(x_start, x_end - x_start)
|
||||||
} else {
|
})
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cursor_from_glyph_left(&self, glyph: &LayoutGlyph) -> Cursor {
|
const fn cursor_from_glyph_left(&self, glyph: &LayoutGlyph) -> Cursor {
|
||||||
if self.rtl {
|
if self.rtl {
|
||||||
Cursor::new_with_affinity(self.line_i, glyph.end, Affinity::Before)
|
Cursor::new_with_affinity(self.line_i, glyph.end, Affinity::Before)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -82,7 +85,7 @@ impl LayoutRun<'_> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cursor_from_glyph_right(&self, glyph: &LayoutGlyph) -> Cursor {
|
const fn cursor_from_glyph_right(&self, glyph: &LayoutGlyph) -> Cursor {
|
||||||
if self.rtl {
|
if self.rtl {
|
||||||
Cursor::new_with_affinity(self.line_i, glyph.start, Affinity::After)
|
Cursor::new_with_affinity(self.line_i, glyph.start, Affinity::After)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -102,7 +105,7 @@ pub struct LayoutRunIter<'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'b> LayoutRunIter<'b> {
|
impl<'b> LayoutRunIter<'b> {
|
||||||
pub fn new(buffer: &'b Buffer) -> Self {
|
pub const fn new(buffer: &'b Buffer) -> Self {
|
||||||
Self {
|
Self {
|
||||||
buffer,
|
buffer,
|
||||||
line_i: buffer.scroll.line,
|
line_i: buffer.scroll.line,
|
||||||
|
|
@ -276,7 +279,7 @@ impl Buffer {
|
||||||
pub fn borrow_with<'a>(
|
pub fn borrow_with<'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
font_system: &'a mut FontSystem,
|
font_system: &'a mut FontSystem,
|
||||||
) -> BorrowedWithFontSystem<'a, Buffer> {
|
) -> BorrowedWithFontSystem<'a, Self> {
|
||||||
BorrowedWithFontSystem {
|
BorrowedWithFontSystem {
|
||||||
inner: self,
|
inner: self,
|
||||||
font_system,
|
font_system,
|
||||||
|
|
@ -358,7 +361,7 @@ impl Buffer {
|
||||||
let layout = self
|
let layout = self
|
||||||
.line_layout(font_system, line_i)
|
.line_layout(font_system, line_i)
|
||||||
.expect("shape_until_cursor failed to scroll forwards");
|
.expect("shape_until_cursor failed to scroll forwards");
|
||||||
for layout_line in layout.iter() {
|
for layout_line in layout {
|
||||||
total_height += layout_line.line_height_opt.unwrap_or(metrics.line_height);
|
total_height += layout_line.line_height_opt.unwrap_or(metrics.line_height);
|
||||||
}
|
}
|
||||||
if total_height > height + self.scroll.vertical {
|
if total_height > height + self.scroll.vertical {
|
||||||
|
|
@ -379,18 +382,16 @@ impl Buffer {
|
||||||
if let Some(layout_cursor) = self.layout_cursor(font_system, cursor) {
|
if let Some(layout_cursor) = self.layout_cursor(font_system, cursor) {
|
||||||
if let Some(layout_lines) = self.line_layout(font_system, layout_cursor.line) {
|
if let Some(layout_lines) = self.line_layout(font_system, layout_cursor.line) {
|
||||||
if let Some(layout_line) = layout_lines.get(layout_cursor.layout) {
|
if let Some(layout_line) = layout_lines.get(layout_cursor.layout) {
|
||||||
let (x_min, x_max) = if let Some(glyph) = layout_line
|
let (x_min, x_max) = layout_line
|
||||||
.glyphs
|
.glyphs
|
||||||
.get(layout_cursor.glyph)
|
.get(layout_cursor.glyph)
|
||||||
.or_else(|| layout_line.glyphs.last())
|
.or_else(|| layout_line.glyphs.last())
|
||||||
{
|
.map_or((0.0, 0.0), |glyph| {
|
||||||
//TODO: use code from cursor_glyph_opt?
|
//TODO: use code from cursor_glyph_opt?
|
||||||
let x_a = glyph.x;
|
let x_a = glyph.x;
|
||||||
let x_b = glyph.x + glyph.w;
|
let x_b = glyph.x + glyph.w;
|
||||||
(x_a.min(x_b), x_a.max(x_b))
|
(x_a.min(x_b), x_a.max(x_b))
|
||||||
} else {
|
});
|
||||||
(0.0, 0.0)
|
|
||||||
};
|
|
||||||
if x_min < self.scroll.horizontal {
|
if x_min < self.scroll.horizontal {
|
||||||
self.scroll.horizontal = x_min;
|
self.scroll.horizontal = x_min;
|
||||||
self.redraw = true;
|
self.redraw = true;
|
||||||
|
|
@ -419,7 +420,7 @@ impl Buffer {
|
||||||
let line_i = self.scroll.line - 1;
|
let line_i = self.scroll.line - 1;
|
||||||
if let Some(layout) = self.line_layout(font_system, line_i) {
|
if let Some(layout) = self.line_layout(font_system, line_i) {
|
||||||
let mut layout_height = 0.0;
|
let mut layout_height = 0.0;
|
||||||
for layout_line in layout.iter() {
|
for layout_line in layout {
|
||||||
layout_height +=
|
layout_height +=
|
||||||
layout_line.line_height_opt.unwrap_or(metrics.line_height);
|
layout_line.line_height_opt.unwrap_or(metrics.line_height);
|
||||||
}
|
}
|
||||||
|
|
@ -451,16 +452,15 @@ impl Buffer {
|
||||||
if prune {
|
if prune {
|
||||||
self.lines[line_i].reset_shaping();
|
self.lines[line_i].reset_shaping();
|
||||||
continue;
|
continue;
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut layout_height = 0.0;
|
let mut layout_height = 0.0;
|
||||||
let layout = self
|
let layout = self
|
||||||
.line_layout(font_system, line_i)
|
.line_layout(font_system, line_i)
|
||||||
.expect("shape_until_scroll invalid line");
|
.expect("shape_until_scroll invalid line");
|
||||||
for layout_line in layout.iter() {
|
for layout_line in layout {
|
||||||
let line_height = layout_line.line_height_opt.unwrap_or(metrics.line_height);
|
let line_height = layout_line.line_height_opt.unwrap_or(metrics.line_height);
|
||||||
layout_height += line_height;
|
layout_height += line_height;
|
||||||
total_height += line_height;
|
total_height += line_height;
|
||||||
|
|
@ -547,7 +547,7 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current [`Metrics`]
|
/// Get the current [`Metrics`]
|
||||||
pub fn metrics(&self) -> Metrics {
|
pub const fn metrics(&self) -> Metrics {
|
||||||
self.metrics
|
self.metrics
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -561,7 +561,7 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current [`Wrap`]
|
/// Get the current [`Wrap`]
|
||||||
pub fn wrap(&self) -> Wrap {
|
pub const fn wrap(&self) -> Wrap {
|
||||||
self.wrap
|
self.wrap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -575,7 +575,7 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current `monospace_width`
|
/// Get the current `monospace_width`
|
||||||
pub fn monospace_width(&self) -> Option<f32> {
|
pub const fn monospace_width(&self) -> Option<f32> {
|
||||||
self.monospace_width
|
self.monospace_width
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -593,7 +593,7 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current `tab_width`
|
/// Get the current `tab_width`
|
||||||
pub fn tab_width(&self) -> u16 {
|
pub const fn tab_width(&self) -> u16 {
|
||||||
self.tab_width
|
self.tab_width
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -606,7 +606,7 @@ impl Buffer {
|
||||||
if tab_width != self.tab_width {
|
if tab_width != self.tab_width {
|
||||||
self.tab_width = tab_width;
|
self.tab_width = tab_width;
|
||||||
// Shaping must be reset when tab width is changed
|
// Shaping must be reset when tab width is changed
|
||||||
for line in self.lines.iter_mut() {
|
for line in &mut self.lines {
|
||||||
if line.shape_opt().is_some() && line.text().contains('\t') {
|
if line.shape_opt().is_some() && line.text().contains('\t') {
|
||||||
line.reset_shaping();
|
line.reset_shaping();
|
||||||
}
|
}
|
||||||
|
|
@ -617,7 +617,7 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current buffer dimensions (width, height)
|
/// Get the current buffer dimensions (width, height)
|
||||||
pub fn size(&self) -> (Option<f32>, Option<f32>) {
|
pub const fn size(&self) -> (Option<f32>, Option<f32>) {
|
||||||
(self.width_opt, self.height_opt)
|
(self.width_opt, self.height_opt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -660,7 +660,7 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the current scroll location
|
/// Get the current scroll location
|
||||||
pub fn scroll(&self) -> Scroll {
|
pub const fn scroll(&self) -> Scroll {
|
||||||
self.scroll
|
self.scroll
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -758,8 +758,7 @@ impl Buffer {
|
||||||
let mut attrs_list = self
|
let mut attrs_list = self
|
||||||
.lines
|
.lines
|
||||||
.get_mut(line_count)
|
.get_mut(line_count)
|
||||||
.map(BufferLine::reclaim_attrs)
|
.map_or_else(|| AttrsList::new(&Attrs::new()), BufferLine::reclaim_attrs)
|
||||||
.unwrap_or_else(|| AttrsList::new(&Attrs::new()))
|
|
||||||
.reset(default_attrs);
|
.reset(default_attrs);
|
||||||
let mut line_string = self
|
let mut line_string = self
|
||||||
.lines
|
.lines
|
||||||
|
|
@ -811,8 +810,7 @@ impl Buffer {
|
||||||
let next_attrs_list = self
|
let next_attrs_list = self
|
||||||
.lines
|
.lines
|
||||||
.get_mut(line_count + 1)
|
.get_mut(line_count + 1)
|
||||||
.map(BufferLine::reclaim_attrs)
|
.map_or_else(|| AttrsList::new(&Attrs::new()), BufferLine::reclaim_attrs)
|
||||||
.unwrap_or_else(|| AttrsList::new(&Attrs::new()))
|
|
||||||
.reset(default_attrs);
|
.reset(default_attrs);
|
||||||
let next_line_string = self
|
let next_line_string = self
|
||||||
.lines
|
.lines
|
||||||
|
|
@ -856,7 +854,7 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// True if a redraw is needed
|
/// True if a redraw is needed
|
||||||
pub fn redraw(&self) -> bool {
|
pub const fn redraw(&self) -> bool {
|
||||||
self.redraw
|
self.redraw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -866,7 +864,7 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the visible layout runs for rendering and other tasks
|
/// Get the visible layout runs for rendering and other tasks
|
||||||
pub fn layout_runs(&self) -> LayoutRunIter {
|
pub const fn layout_runs(&self) -> LayoutRunIter {
|
||||||
LayoutRunIter::new(self)
|
LayoutRunIter::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -991,14 +989,16 @@ impl Buffer {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let (new_index, new_affinity) = match layout_line.glyphs.get(layout_cursor.glyph) {
|
let (new_index, new_affinity) =
|
||||||
Some(glyph) => (glyph.start, Affinity::After),
|
layout_line.glyphs.get(layout_cursor.glyph).map_or_else(
|
||||||
None => match layout_line.glyphs.last() {
|
|| {
|
||||||
Some(glyph) => (glyph.end, Affinity::Before),
|
layout_line
|
||||||
//TODO: is this correct?
|
.glyphs
|
||||||
None => (0, Affinity::After),
|
.last()
|
||||||
},
|
.map_or((0, Affinity::After), |glyph| (glyph.end, Affinity::Before))
|
||||||
};
|
},
|
||||||
|
|glyph| (glyph.start, Affinity::After),
|
||||||
|
);
|
||||||
|
|
||||||
if cursor.line != layout_cursor.line
|
if cursor.line != layout_cursor.line
|
||||||
|| cursor.index != new_index
|
|| cursor.index != new_index
|
||||||
|
|
@ -1159,8 +1159,7 @@ impl Buffer {
|
||||||
cursor.index = line
|
cursor.index = line
|
||||||
.text()
|
.text()
|
||||||
.char_indices()
|
.char_indices()
|
||||||
.filter_map(|(i, c)| if c.is_whitespace() { None } else { Some(i) })
|
.find_map(|(i, c)| if c.is_whitespace() { None } else { Some(i) })
|
||||||
.next()
|
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
cursor_x_opt = None;
|
cursor_x_opt = None;
|
||||||
}
|
}
|
||||||
|
|
@ -1253,7 +1252,7 @@ impl Buffer {
|
||||||
.unicode_word_indices()
|
.unicode_word_indices()
|
||||||
.map(|(i, word)| i + word.len())
|
.map(|(i, word)| i + word.len())
|
||||||
.find(|&i| i > cursor.index)
|
.find(|&i| i > cursor.index)
|
||||||
.unwrap_or(line.text().len());
|
.unwrap_or_else(|| line.text().len());
|
||||||
} else if cursor.line + 1 < self.lines.len() {
|
} else if cursor.line + 1 < self.lines.len() {
|
||||||
cursor.line += 1;
|
cursor.line += 1;
|
||||||
cursor.index = 0;
|
cursor.index = 0;
|
||||||
|
|
@ -1340,13 +1339,9 @@ impl Buffer {
|
||||||
F: FnMut(i32, i32, u32, u32, Color),
|
F: FnMut(i32, i32, u32, u32, Color),
|
||||||
{
|
{
|
||||||
for run in self.layout_runs() {
|
for run in self.layout_runs() {
|
||||||
for glyph in run.glyphs.iter() {
|
for glyph in run.glyphs {
|
||||||
let physical_glyph = glyph.physical((0., 0.), 1.0);
|
let physical_glyph = glyph.physical((0., 0.), 1.0);
|
||||||
|
let glyph_color = glyph.color_opt.map_or(color, |some| some);
|
||||||
let glyph_color = match glyph.color_opt {
|
|
||||||
Some(some) => some,
|
|
||||||
None => color,
|
|
||||||
};
|
|
||||||
|
|
||||||
cache.with_pixels(
|
cache.with_pixels(
|
||||||
font_system,
|
font_system,
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ impl BufferLine {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get line ending
|
/// Get line ending
|
||||||
pub fn ending(&self) -> LineEnding {
|
pub const fn ending(&self) -> LineEnding {
|
||||||
self.ending
|
self.ending
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,7 +114,7 @@ impl BufferLine {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get attributes list
|
/// Get attributes list
|
||||||
pub fn attrs_list(&self) -> &AttrsList {
|
pub const fn attrs_list(&self) -> &AttrsList {
|
||||||
&self.attrs_list
|
&self.attrs_list
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,7 +133,7 @@ impl BufferLine {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the Text alignment
|
/// Get the Text alignment
|
||||||
pub fn align(&self) -> Option<Align> {
|
pub const fn align(&self) -> Option<Align> {
|
||||||
self.align
|
self.align
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,7 +155,7 @@ impl BufferLine {
|
||||||
/// Append line at end of this line
|
/// Append line at end of this line
|
||||||
///
|
///
|
||||||
/// The wrap setting of the appended line will be lost
|
/// The wrap setting of the appended line will be lost
|
||||||
pub fn append(&mut self, other: Self) {
|
pub fn append(&mut self, other: &Self) {
|
||||||
let len = self.text.len();
|
let len = self.text.len();
|
||||||
self.text.push_str(other.text());
|
self.text.push_str(other.text());
|
||||||
|
|
||||||
|
|
@ -224,7 +224,7 @@ impl BufferLine {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get line shaping cache
|
/// Get line shaping cache
|
||||||
pub fn shape_opt(&self) -> Option<&ShapeLine> {
|
pub const fn shape_opt(&self) -> Option<&ShapeLine> {
|
||||||
self.shape_opt.get()
|
self.shape_opt.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -261,13 +261,13 @@ impl BufferLine {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get line layout cache
|
/// Get line layout cache
|
||||||
pub fn layout_opt(&self) -> Option<&Vec<LayoutLine>> {
|
pub const fn layout_opt(&self) -> Option<&Vec<LayoutLine>> {
|
||||||
self.layout_opt.get()
|
self.layout_opt.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get line metadata. This will be None if [`BufferLine::set_metadata`] has not been called
|
/// Get line metadata. This will be None if [`BufferLine::set_metadata`] has not been called
|
||||||
/// after the last reset of shaping and layout caches
|
/// after the last reset of shaping and layout caches
|
||||||
pub fn metadata(&self) -> Option<usize> {
|
pub const fn metadata(&self) -> Option<usize> {
|
||||||
self.metadata
|
self.metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ pub enum Cached<T: Clone + Debug> {
|
||||||
|
|
||||||
impl<T: Clone + Debug> Cached<T> {
|
impl<T: Clone + Debug> Cached<T> {
|
||||||
/// Gets the value if in state `Self::Used`.
|
/// Gets the value if in state `Self::Used`.
|
||||||
pub fn get(&self) -> Option<&T> {
|
pub const fn get(&self) -> Option<&T> {
|
||||||
match self {
|
match self {
|
||||||
Self::Empty | Self::Unused(_) => None,
|
Self::Empty | Self::Unused(_) => None,
|
||||||
Self::Used(t) => Some(t),
|
Self::Used(t) => Some(t),
|
||||||
|
|
@ -27,7 +27,7 @@ impl<T: Clone + Debug> Cached<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the value is empty or unused.
|
/// Checks if the value is empty or unused.
|
||||||
pub fn is_unused(&self) -> bool {
|
pub const fn is_unused(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Empty | Self::Unused(_) => true,
|
Self::Empty | Self::Unused(_) => true,
|
||||||
Self::Used(_) => false,
|
Self::Used(_) => false,
|
||||||
|
|
@ -35,7 +35,7 @@ impl<T: Clone + Debug> Cached<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the value is used (i.e. cached for access).
|
/// Checks if the value is used (i.e. cached for access).
|
||||||
pub fn is_used(&self) -> bool {
|
pub const fn is_used(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Self::Empty | Self::Unused(_) => false,
|
Self::Empty | Self::Unused(_) => false,
|
||||||
Self::Used(_) => true,
|
Self::Used(_) => true,
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ impl Affinity {
|
||||||
*self == Self::After
|
*self == Self::After
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_before(before: bool) -> Self {
|
pub const fn from_before(before: bool) -> Self {
|
||||||
if before {
|
if before {
|
||||||
Self::Before
|
Self::Before
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -51,7 +51,7 @@ impl Affinity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_after(after: bool) -> Self {
|
pub const fn from_after(after: bool) -> Self {
|
||||||
if after {
|
if after {
|
||||||
Self::After
|
Self::After
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,24 @@
|
||||||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||||
|
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
use alloc::{
|
||||||
|
string::{String, ToString},
|
||||||
|
vec::Vec,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "swash")]
|
||||||
|
use std::cmp;
|
||||||
|
|
||||||
|
use core::iter::once;
|
||||||
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
#[cfg(feature = "swash")]
|
#[cfg(feature = "swash")]
|
||||||
use crate::Color;
|
use crate::Color;
|
||||||
use crate::{
|
use crate::{
|
||||||
Action, Attrs, AttrsList, BorrowedWithFontSystem, BufferLine, BufferRef, Change, ChangeItem,
|
Action, Attrs, AttrsList, BorrowedWithFontSystem, BufferLine, BufferRef, Change, ChangeItem,
|
||||||
Cursor, Edit, FontSystem, LayoutRun, Selection, Shaping,
|
Cursor, Edit, FontSystem, LayoutRun, Selection, Shaping,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "no_std")]
|
|
||||||
use alloc::{
|
|
||||||
string::{String, ToString},
|
|
||||||
vec::Vec,
|
|
||||||
};
|
|
||||||
#[cfg(feature = "swash")]
|
|
||||||
use std::cmp;
|
|
||||||
|
|
||||||
use core::iter::once;
|
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
|
||||||
/// A wrapper of [`Buffer`] for easy editing
|
/// A wrapper of [`Buffer`] for easy editing
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Editor<'buffer> {
|
pub struct Editor<'buffer> {
|
||||||
|
|
@ -66,30 +69,24 @@ fn cursor_glyph_opt(cursor: &Cursor, run: &LayoutRun) -> Option<(usize, f32)> {
|
||||||
|
|
||||||
fn cursor_position(cursor: &Cursor, run: &LayoutRun) -> Option<(i32, i32)> {
|
fn cursor_position(cursor: &Cursor, run: &LayoutRun) -> Option<(i32, i32)> {
|
||||||
let (cursor_glyph, cursor_glyph_offset) = cursor_glyph_opt(cursor, run)?;
|
let (cursor_glyph, cursor_glyph_offset) = cursor_glyph_opt(cursor, run)?;
|
||||||
let x = match run.glyphs.get(cursor_glyph) {
|
let x = run.glyphs.get(cursor_glyph).map_or_else(
|
||||||
Some(glyph) => {
|
|| {
|
||||||
// Start of detected glyph
|
run.glyphs.last().map_or(0, |glyph| {
|
||||||
if glyph.level.is_rtl() {
|
|
||||||
(glyph.x + glyph.w - cursor_glyph_offset) as i32
|
|
||||||
} else {
|
|
||||||
(glyph.x + cursor_glyph_offset) as i32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => match run.glyphs.last() {
|
|
||||||
Some(glyph) => {
|
|
||||||
// End of last glyph
|
|
||||||
if glyph.level.is_rtl() {
|
if glyph.level.is_rtl() {
|
||||||
glyph.x as i32
|
glyph.x as i32
|
||||||
} else {
|
} else {
|
||||||
(glyph.x + glyph.w) as i32
|
(glyph.x + glyph.w) as i32
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
None => {
|
},
|
||||||
// Start of empty line
|
|glyph| {
|
||||||
0
|
if glyph.level.is_rtl() {
|
||||||
|
(glyph.x + glyph.w - cursor_glyph_offset) as i32
|
||||||
|
} else {
|
||||||
|
(glyph.x + cursor_glyph_offset) as i32
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
);
|
||||||
|
|
||||||
Some((x, run.line_top as i32))
|
Some((x, run.line_top as i32))
|
||||||
}
|
}
|
||||||
|
|
@ -135,7 +132,7 @@ impl<'buffer> Editor<'buffer> {
|
||||||
if let Some((start, end)) = selection_bounds {
|
if let Some((start, end)) = selection_bounds {
|
||||||
if line_i >= start.line && line_i <= end.line {
|
if line_i >= start.line && line_i <= end.line {
|
||||||
let mut range_opt = None;
|
let mut range_opt = None;
|
||||||
for glyph in run.glyphs.iter() {
|
for glyph in run.glyphs {
|
||||||
// Guess x offset based on characters
|
// Guess x offset based on characters
|
||||||
let cluster = &run.text[glyph.start..glyph.end];
|
let cluster = &run.text[glyph.start..glyph.end];
|
||||||
let total = cluster.grapheme_indices(true).count();
|
let total = cluster.grapheme_indices(true).count();
|
||||||
|
|
@ -197,13 +194,10 @@ impl<'buffer> Editor<'buffer> {
|
||||||
f(x, y, 1, line_height as u32, cursor_color);
|
f(x, y, 1, line_height as u32, cursor_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
for glyph in run.glyphs.iter() {
|
for glyph in run.glyphs {
|
||||||
let physical_glyph = glyph.physical((0., 0.), 1.0);
|
let physical_glyph = glyph.physical((0., 0.), 1.0);
|
||||||
|
|
||||||
let mut glyph_color = match glyph.color_opt {
|
let mut glyph_color = glyph.color_opt.map_or(text_color, |some| some);
|
||||||
Some(some) => some,
|
|
||||||
None => text_color,
|
|
||||||
};
|
|
||||||
if text_color != selected_text_color {
|
if text_color != selected_text_color {
|
||||||
if let Some((start, end)) = selection_bounds {
|
if let Some((start, end)) = selection_bounds {
|
||||||
if line_i >= start.line
|
if line_i >= start.line
|
||||||
|
|
@ -277,7 +271,7 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tab_width(&self) -> u16 {
|
fn tab_width(&self) -> u16 {
|
||||||
self.with_buffer(|buffer| buffer.tab_width())
|
self.with_buffer(super::super::buffer::Buffer::tab_width)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_tab_width(&mut self, font_system: &mut FontSystem, tab_width: u16) {
|
fn set_tab_width(&mut self, font_system: &mut FontSystem, tab_width: u16) {
|
||||||
|
|
@ -334,12 +328,12 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
|
||||||
|
|
||||||
// Re-add part of line after selection
|
// Re-add part of line after selection
|
||||||
if let Some(after) = after_opt {
|
if let Some(after) = after_opt {
|
||||||
buffer.lines[start.line].append(after);
|
buffer.lines[start.line].append(&after);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-add valid parts of end line
|
// Re-add valid parts of end line
|
||||||
if let Some(end_line) = end_line_opt {
|
if let Some(end_line) = end_line_opt {
|
||||||
buffer.lines[start.line].append(end_line);
|
buffer.lines[start.line].append(&end_line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -376,7 +370,7 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
|
||||||
let ending = buffer
|
let ending = buffer
|
||||||
.lines
|
.lines
|
||||||
.last()
|
.last()
|
||||||
.map(|line| line.ending())
|
.map(super::super::buffer_line::BufferLine::ending)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let line = BufferLine::new(
|
let line = BufferLine::new(
|
||||||
String::new(),
|
String::new(),
|
||||||
|
|
@ -386,7 +380,7 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
|
||||||
buffer
|
buffer
|
||||||
.lines
|
.lines
|
||||||
.last()
|
.last()
|
||||||
.map_or(Attrs::new(), |line| line.attrs_list().defaults())
|
.map_or_else(Attrs::new, |line| line.attrs_list().defaults())
|
||||||
},
|
},
|
||||||
|x| x.defaults(),
|
|x| x.defaults(),
|
||||||
)),
|
)),
|
||||||
|
|
@ -417,7 +411,7 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
|
||||||
let mut these_attrs = final_attrs.split_off(data_line.len());
|
let mut these_attrs = final_attrs.split_off(data_line.len());
|
||||||
remaining_split_len -= data_line.len();
|
remaining_split_len -= data_line.len();
|
||||||
core::mem::swap(&mut these_attrs, &mut final_attrs);
|
core::mem::swap(&mut these_attrs, &mut final_attrs);
|
||||||
line.append(BufferLine::new(
|
line.append(&BufferLine::new(
|
||||||
data_line
|
data_line
|
||||||
.strip_suffix(char::is_control)
|
.strip_suffix(char::is_control)
|
||||||
.unwrap_or(data_line),
|
.unwrap_or(data_line),
|
||||||
|
|
@ -438,11 +432,11 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
|
||||||
final_attrs.split_off(remaining_split_len),
|
final_attrs.split_off(remaining_split_len),
|
||||||
Shaping::Advanced,
|
Shaping::Advanced,
|
||||||
);
|
);
|
||||||
tmp.append(after);
|
tmp.append(&after);
|
||||||
buffer.lines.insert(insert_line, tmp);
|
buffer.lines.insert(insert_line, tmp);
|
||||||
cursor.line += 1;
|
cursor.line += 1;
|
||||||
} else {
|
} else {
|
||||||
line.append(after);
|
line.append(&after);
|
||||||
}
|
}
|
||||||
for data_line in lines_iter.rev() {
|
for data_line in lines_iter.rev() {
|
||||||
remaining_split_len -= data_line.len();
|
remaining_split_len -= data_line.len();
|
||||||
|
|
@ -510,9 +504,8 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_selection(&mut self) -> bool {
|
fn delete_selection(&mut self) -> bool {
|
||||||
let (start, end) = match self.selection_bounds() {
|
let Some((start, end)) = self.selection_bounds() else {
|
||||||
Some(some) => some,
|
return false;
|
||||||
None => return false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Reset cursor to start of selection
|
// Reset cursor to start of selection
|
||||||
|
|
@ -538,7 +531,7 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for item in change.items.iter() {
|
for item in &change.items {
|
||||||
//TODO: edit cursor if needed?
|
//TODO: edit cursor if needed?
|
||||||
if item.insert {
|
if item.insert {
|
||||||
self.cursor = self.insert_at(item.start, &item.text, None);
|
self.cursor = self.insert_at(item.start, &item.text, None);
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ impl Change {
|
||||||
// Reverse change (in place)
|
// Reverse change (in place)
|
||||||
pub fn reverse(&mut self) {
|
pub fn reverse(&mut self) {
|
||||||
self.items.reverse();
|
self.items.reverse();
|
||||||
for item in self.items.iter_mut() {
|
for item in &mut self.items {
|
||||||
item.reverse();
|
item.reverse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -192,7 +192,7 @@ pub trait Edit<'buffer> {
|
||||||
|
|
||||||
/// Get the [`Buffer`] redraw flag
|
/// Get the [`Buffer`] redraw flag
|
||||||
fn redraw(&self) -> bool {
|
fn redraw(&self) -> bool {
|
||||||
self.with_buffer(|buffer| buffer.redraw())
|
self.with_buffer(super::buffer::Buffer::redraw)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the [`Buffer`] redraw flag
|
/// Set the [`Buffer`] redraw flag
|
||||||
|
|
@ -273,7 +273,7 @@ pub trait Edit<'buffer> {
|
||||||
.unicode_word_indices()
|
.unicode_word_indices()
|
||||||
.map(|(i, word)| i + word.len())
|
.map(|(i, word)| i + word.len())
|
||||||
.find(|&i| i > end.index)
|
.find(|&i| i > end.index)
|
||||||
.unwrap_or(line.text().len());
|
.unwrap_or_else(|| line.text().len());
|
||||||
}
|
}
|
||||||
|
|
||||||
Some((start, end))
|
Some((start, end))
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#[cfg(feature = "no_std")]
|
|
||||||
use core::cmp;
|
|
||||||
|
|
||||||
use alloc::{collections::BTreeMap, string::String};
|
use alloc::{collections::BTreeMap, string::String};
|
||||||
|
#[cfg(feature = "swash")]
|
||||||
|
use core::cmp;
|
||||||
use modit::{Event, Key, Parser, TextObject, WordIter};
|
use modit::{Event, Key, Parser, TextObject, WordIter};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ pub trait Fallback: Send + Sync {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub(crate) struct Fallbacks {
|
pub struct Fallbacks {
|
||||||
lists: Vec<&'static str>,
|
lists: Vec<&'static str>,
|
||||||
common_fallback_range: Range<usize>,
|
common_fallback_range: Range<usize>,
|
||||||
forbidden_fallback_range: Range<usize>,
|
forbidden_fallback_range: Range<usize>,
|
||||||
|
|
@ -174,13 +174,14 @@ use log::warn as missing_warn;
|
||||||
// Default font gets None for both `weight_offset` and `script_non_matches`, and thus, it is
|
// Default font gets None for both `weight_offset` and `script_non_matches`, and thus, it is
|
||||||
// always the first to be popped from the set.
|
// always the first to be popped from the set.
|
||||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub(crate) struct MonospaceFallbackInfo {
|
pub struct MonospaceFallbackInfo {
|
||||||
font_weight_diff: Option<u16>,
|
font_weight_diff: Option<u16>,
|
||||||
codepoint_non_matches: Option<usize>,
|
codepoint_non_matches: Option<usize>,
|
||||||
font_weight: u16,
|
font_weight: u16,
|
||||||
id: fontdb::ID,
|
id: fontdb::ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct FontFallbackIter<'a> {
|
pub struct FontFallbackIter<'a> {
|
||||||
font_system: &'a mut FontSystem,
|
font_system: &'a mut FontSystem,
|
||||||
font_match_keys: &'a [FontMatchKey],
|
font_match_keys: &'a [FontMatchKey],
|
||||||
|
|
@ -223,7 +224,7 @@ impl<'a> FontFallbackIter<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_missing(&mut self, word: &str) {
|
pub fn check_missing(&self, word: &str) {
|
||||||
if self.end {
|
if self.end {
|
||||||
missing_warn!(
|
missing_warn!(
|
||||||
"Failed to find any fallback for {:?} locale '{}': '{}'",
|
"Failed to find any fallback for {:?} locale '{}': '{}'",
|
||||||
|
|
@ -252,15 +253,16 @@ impl<'a> FontFallbackIter<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn face_name(&self, id: fontdb::ID) -> &str {
|
pub fn face_name(&self, id: fontdb::ID) -> &str {
|
||||||
if let Some(face) = self.font_system.db().face(id) {
|
self.font_system
|
||||||
if let Some((name, _)) = face.families.first() {
|
.db()
|
||||||
name
|
.face(id)
|
||||||
} else {
|
.map_or("invalid font id", |face| {
|
||||||
&face.post_script_name
|
if let Some((name, _)) = face.families.first() {
|
||||||
}
|
name
|
||||||
} else {
|
} else {
|
||||||
"invalid font id"
|
&face.post_script_name
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shape_caches(&mut self) -> &mut ShapeBuffer {
|
pub fn shape_caches(&mut self) -> &mut ShapeBuffer {
|
||||||
|
|
@ -268,11 +270,10 @@ impl<'a> FontFallbackIter<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn face_contains_family(&self, id: fontdb::ID, family_name: &str) -> bool {
|
fn face_contains_family(&self, id: fontdb::ID, family_name: &str) -> bool {
|
||||||
if let Some(face) = self.font_system.db().face(id) {
|
self.font_system
|
||||||
face.families.iter().any(|(name, _)| name == family_name)
|
.db()
|
||||||
} else {
|
.face(id)
|
||||||
false
|
.is_some_and(|face| face.families.iter().any(|(name, _)| name == family_name))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_font_match_key(&self) -> Option<&FontMatchKey> {
|
fn default_font_match_key(&self) -> Option<&FontMatchKey> {
|
||||||
|
|
@ -304,7 +305,7 @@ impl<'a> FontFallbackIter<'a> {
|
||||||
'DEF_FAM: while self.default_i < self.default_families.len() {
|
'DEF_FAM: while self.default_i < self.default_families.len() {
|
||||||
self.default_i += 1;
|
self.default_i += 1;
|
||||||
let is_mono = self.default_families[self.default_i - 1] == &Family::Monospace;
|
let is_mono = self.default_families[self.default_i - 1] == &Family::Monospace;
|
||||||
let default_font_match_key = self.default_font_match_key().cloned();
|
let default_font_match_key = self.default_font_match_key().copied();
|
||||||
let word_chars_count = self.word.chars().count();
|
let word_chars_count = self.word.chars().count();
|
||||||
|
|
||||||
macro_rules! mk_mono_fallback_info {
|
macro_rules! mk_mono_fallback_info {
|
||||||
|
|
@ -334,9 +335,8 @@ impl<'a> FontFallbackIter<'a> {
|
||||||
(false, Some(m_key)) => {
|
(false, Some(m_key)) => {
|
||||||
if let Some(font) = self.font_system.get_font(m_key.id, self.ideal_weight) {
|
if let Some(font) = self.font_system.get_font(m_key.id, self.ideal_weight) {
|
||||||
return Some(font);
|
return Some(font);
|
||||||
} else {
|
|
||||||
break 'DEF_FAM;
|
|
||||||
}
|
}
|
||||||
|
break 'DEF_FAM;
|
||||||
}
|
}
|
||||||
(true, None) => (),
|
(true, None) => (),
|
||||||
(true, Some(m_key)) => {
|
(true, Some(m_key)) => {
|
||||||
|
|
@ -360,7 +360,7 @@ impl<'a> FontFallbackIter<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
let mono_ids_for_scripts = if is_mono && !self.scripts.is_empty() {
|
let mono_ids_for_scripts = if is_mono && !self.scripts.is_empty() {
|
||||||
let scripts = self.scripts.iter().filter_map(|script| {
|
let scripts = self.scripts.iter().filter_map(|script| {
|
||||||
|
|
|
||||||
|
|
@ -27,16 +27,16 @@ impl Fallback for PlatformFallback {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallbacks to use after any script specific fallbacks
|
// Fallbacks to use after any script specific fallbacks
|
||||||
fn common_fallback() -> &'static [&'static str] {
|
const fn common_fallback() -> &'static [&'static str] {
|
||||||
&[]
|
&[]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallbacks to never use
|
// Fallbacks to never use
|
||||||
fn forbidden_fallback() -> &'static [&'static str] {
|
const fn forbidden_fallback() -> &'static [&'static str] {
|
||||||
&[]
|
&[]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallbacks to use per script
|
// Fallbacks to use per script
|
||||||
fn script_fallback(_script: Script, _locale: &str) -> &'static [&'static str] {
|
const fn script_fallback(_script: Script, _locale: &str) -> &'static [&'static str] {
|
||||||
&[]
|
&[]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ impl Fallback for PlatformFallback {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallbacks to use after any script specific fallbacks
|
// Fallbacks to use after any script specific fallbacks
|
||||||
fn common_fallback() -> &'static [&'static str] {
|
const fn common_fallback() -> &'static [&'static str] {
|
||||||
//TODO: abstract style (sans/serif/monospaced)
|
//TODO: abstract style (sans/serif/monospaced)
|
||||||
&[
|
&[
|
||||||
/* Sans-serif fallbacks */
|
/* Sans-serif fallbacks */
|
||||||
|
|
@ -49,7 +49,7 @@ fn common_fallback() -> &'static [&'static str] {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallbacks to never use
|
// Fallbacks to never use
|
||||||
fn forbidden_fallback() -> &'static [&'static str] {
|
const fn forbidden_fallback() -> &'static [&'static str] {
|
||||||
&[]
|
&[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ use alloc::vec::Vec;
|
||||||
use rustybuzz::Face as RustybuzzFace;
|
use rustybuzz::Face as RustybuzzFace;
|
||||||
use self_cell::self_cell;
|
use self_cell::self_cell;
|
||||||
|
|
||||||
pub(crate) mod fallback;
|
pub mod fallback;
|
||||||
pub use fallback::{Fallback, PlatformFallback};
|
pub use fallback::{Fallback, PlatformFallback};
|
||||||
|
|
||||||
pub use self::system::*;
|
pub use self::system::*;
|
||||||
|
|
@ -58,7 +58,7 @@ impl fmt::Debug for Font {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Font {
|
impl Font {
|
||||||
pub fn id(&self) -> fontdb::ID {
|
pub const fn id(&self) -> fontdb::ID {
|
||||||
self.id
|
self.id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,9 +119,9 @@ impl Font {
|
||||||
let monospace_em_width = info
|
let monospace_em_width = info
|
||||||
.monospaced
|
.monospaced
|
||||||
.then(|| {
|
.then(|| {
|
||||||
let hor_advance = face.glyph_hor_advance(face.glyph_index(' ')?)? as f32;
|
let hor_advance = face.glyph_hor_advance(face.glyph_index(' ')?)?;
|
||||||
let upem = face.units_per_em() as f32;
|
let upem = face.units_per_em();
|
||||||
Some(hor_advance / upem)
|
Some(f32::from(hor_advance) / f32::from(upem))
|
||||||
})
|
})
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
|
|
@ -144,7 +144,7 @@ impl Font {
|
||||||
.cmap?
|
.cmap?
|
||||||
.subtables
|
.subtables
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|subtable| subtable.is_unicode())
|
.filter(ttf_parser::cmap::Subtable::is_unicode)
|
||||||
.for_each(|subtable| {
|
.for_each(|subtable| {
|
||||||
unicode_codepoints.reserve(1024);
|
unicode_codepoints.reserve(1024);
|
||||||
subtable.codepoints(|code_point| {
|
subtable.codepoints(|code_point| {
|
||||||
|
|
@ -194,7 +194,7 @@ impl Font {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.find(|axis| axis.tag == ttf_parser::Tag::from_bytes(b"wght"))
|
.find(|axis| axis.tag == ttf_parser::Tag::from_bytes(b"wght"))
|
||||||
{
|
{
|
||||||
let wght = (weight.0 as f32).clamp(axis.min_value, axis.max_value);
|
let wght = f32::from(weight.0).clamp(axis.min_value, axis.max_value);
|
||||||
let _ = face.set_variation(ttf_parser::Tag::from_bytes(b"wght"), wght);
|
let _ = face.set_variation(ttf_parser::Tag::from_bytes(b"wght"), wght);
|
||||||
}
|
}
|
||||||
face
|
face
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,7 @@ impl fmt::Debug for FontSystem {
|
||||||
f.debug_struct("FontSystem")
|
f.debug_struct("FontSystem")
|
||||||
.field("locale", &self.locale)
|
.field("locale", &self.locale)
|
||||||
.field("db", &self.db)
|
.field("db", &self.db)
|
||||||
.finish()
|
.finish_non_exhaustive()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,7 +180,7 @@ impl FontSystem {
|
||||||
HashMap::default();
|
HashMap::default();
|
||||||
|
|
||||||
if cfg!(feature = "monospace_fallback") {
|
if cfg!(feature = "monospace_fallback") {
|
||||||
monospace_font_ids.iter().for_each(|&id| {
|
for &id in &monospace_font_ids {
|
||||||
db.with_face_data(id, |font_data, face_index| {
|
db.with_face_data(id, |font_data, face_index| {
|
||||||
let _ = ttf_parser::Face::parse(font_data, face_index).map(|face| {
|
let _ = ttf_parser::Face::parse(font_data, face_index).map(|face| {
|
||||||
face.tables()
|
face.tables()
|
||||||
|
|
@ -196,7 +196,7 @@ impl FontSystem {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let per_script_monospace_font_ids = per_script_monospace_font_ids
|
let per_script_monospace_font_ids = per_script_monospace_font_ids
|
||||||
|
|
@ -211,9 +211,9 @@ impl FontSystem {
|
||||||
db,
|
db,
|
||||||
monospace_font_ids,
|
monospace_font_ids,
|
||||||
per_script_monospace_font_ids,
|
per_script_monospace_font_ids,
|
||||||
font_cache: Default::default(),
|
font_cache: HashMap::default(),
|
||||||
font_matches_cache: Default::default(),
|
font_matches_cache: HashMap::default(),
|
||||||
font_codepoint_support_info_cache: Default::default(),
|
font_codepoint_support_info_cache: HashMap::default(),
|
||||||
monospace_fallbacks_buffer: BTreeSet::default(),
|
monospace_fallbacks_buffer: BTreeSet::default(),
|
||||||
#[cfg(feature = "shape-run-cache")]
|
#[cfg(feature = "shape-run-cache")]
|
||||||
shape_run_cache: crate::ShapeRunCache::default(),
|
shape_run_cache: crate::ShapeRunCache::default(),
|
||||||
|
|
@ -234,7 +234,7 @@ impl FontSystem {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the database.
|
/// Get the database.
|
||||||
pub fn db(&self) -> &fontdb::Database {
|
pub const fn db(&self) -> &fontdb::Database {
|
||||||
&self.db
|
&self.db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -258,15 +258,14 @@ impl FontSystem {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.db.make_shared_face_data(id);
|
self.db.make_shared_face_data(id);
|
||||||
}
|
}
|
||||||
match Font::new(&self.db, id, weight) {
|
if let Some(font) = Font::new(&self.db, id, weight) {
|
||||||
Some(font) => Some(Arc::new(font)),
|
Some(Arc::new(font))
|
||||||
None => {
|
} else {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"failed to load font '{}'",
|
"failed to load font '{}'",
|
||||||
self.db.face(id)?.post_script_name
|
self.db.face(id)?.post_script_name
|
||||||
);
|
);
|
||||||
None
|
None
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.clone()
|
.clone()
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ impl SubpixelBin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_float(&self) -> f32 {
|
pub const fn as_float(&self) -> f32 {
|
||||||
match self {
|
match self {
|
||||||
Self::Zero => 0.0,
|
Self::Zero => 0.0,
|
||||||
Self::One => 0.25,
|
Self::One => 0.25,
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,12 @@
|
||||||
|
|
||||||
use core::fmt::Display;
|
use core::fmt::Display;
|
||||||
|
|
||||||
|
use crate::{math, CacheKey, CacheKeyFlags, Color};
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
use crate::{math, CacheKey, CacheKeyFlags, Color};
|
#[cfg(not(feature = "std"))]
|
||||||
|
use core_maths::CoreFloat;
|
||||||
|
|
||||||
/// A laid out glyph
|
/// A laid out glyph
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
|
@ -78,8 +80,8 @@ impl LayoutGlyph {
|
||||||
self.glyph_id,
|
self.glyph_id,
|
||||||
self.font_size * scale,
|
self.font_size * scale,
|
||||||
(
|
(
|
||||||
(self.x + x_offset) * scale + offset.0,
|
(self.x + x_offset).mul_add(scale, offset.0),
|
||||||
math::truncf((self.y - y_offset) * scale + offset.1), // Hinting in Y axis
|
math::truncf((self.y - y_offset).mul_add(scale, offset.1)), // Hinting in Y axis
|
||||||
),
|
),
|
||||||
self.font_weight,
|
self.font_weight,
|
||||||
self.cache_key_flags,
|
self.cache_key_flags,
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ pub enum LineEnding {
|
||||||
|
|
||||||
impl LineEnding {
|
impl LineEnding {
|
||||||
/// Get the line ending as a str
|
/// Get the line ending as a str
|
||||||
pub fn as_str(&self) -> &'static str {
|
pub const fn as_str(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::Lf => "\n",
|
Self::Lf => "\n",
|
||||||
Self::CrLf => "\r\n",
|
Self::CrLf => "\r\n",
|
||||||
|
|
@ -39,7 +39,7 @@ pub struct LineIter<'a> {
|
||||||
|
|
||||||
impl<'a> LineIter<'a> {
|
impl<'a> LineIter<'a> {
|
||||||
/// Create an iterator of lines in a string slice
|
/// Create an iterator of lines in a string slice
|
||||||
pub fn new(string: &'a str) -> Self {
|
pub const fn new(string: &'a str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
string,
|
string,
|
||||||
start: 0,
|
start: 0,
|
||||||
|
|
@ -61,9 +61,9 @@ impl Iterator for LineIter<'_> {
|
||||||
LineEnding::CrLf
|
LineEnding::CrLf
|
||||||
} else if after.starts_with("\n\r") {
|
} else if after.starts_with("\n\r") {
|
||||||
LineEnding::LfCr
|
LineEnding::LfCr
|
||||||
} else if after.starts_with("\n") {
|
} else if after.starts_with('\n') {
|
||||||
LineEnding::Lf
|
LineEnding::Lf
|
||||||
} else if after.starts_with("\r") {
|
} else if after.starts_with('\r') {
|
||||||
LineEnding::Cr
|
LineEnding::Cr
|
||||||
} else {
|
} else {
|
||||||
//TODO: this should not be possible
|
//TODO: this should not be possible
|
||||||
|
|
|
||||||
137
src/shape.rs
137
src/shape.rs
|
|
@ -2,20 +2,23 @@
|
||||||
|
|
||||||
#![allow(clippy::too_many_arguments)]
|
#![allow(clippy::too_many_arguments)]
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
use core::cmp::{max, min};
|
|
||||||
use core::fmt;
|
|
||||||
use core::mem;
|
|
||||||
use core::ops::Range;
|
|
||||||
use unicode_script::{Script, UnicodeScript};
|
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
|
||||||
|
|
||||||
use crate::fallback::FontFallbackIter;
|
use crate::fallback::FontFallbackIter;
|
||||||
use crate::{
|
use crate::{
|
||||||
math, Align, AttrsList, CacheKeyFlags, Color, Font, FontSystem, LayoutGlyph, LayoutLine,
|
math, Align, AttrsList, CacheKeyFlags, Color, Font, FontSystem, LayoutGlyph, LayoutLine,
|
||||||
Metrics, Wrap,
|
Metrics, Wrap,
|
||||||
};
|
};
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
|
use core::cmp::{max, min};
|
||||||
|
use core::fmt;
|
||||||
|
use core::mem;
|
||||||
|
use core::ops::Range;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
use core_maths::CoreFloat;
|
||||||
|
use unicode_script::{Script, UnicodeScript};
|
||||||
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
/// The shaping strategy of some text.
|
/// The shaping strategy of some text.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
|
@ -117,8 +120,8 @@ fn shape_fallback(
|
||||||
let run = &line[start_run..end_run];
|
let run = &line[start_run..end_run];
|
||||||
|
|
||||||
let font_scale = font.rustybuzz().units_per_em() as f32;
|
let font_scale = font.rustybuzz().units_per_em() as f32;
|
||||||
let ascent = font.rustybuzz().ascender() as f32 / font_scale;
|
let ascent = f32::from(font.rustybuzz().ascender()) / font_scale;
|
||||||
let descent = -font.rustybuzz().descender() as f32 / font_scale;
|
let descent = -f32::from(font.rustybuzz().descender()) / font_scale;
|
||||||
|
|
||||||
let mut buffer = scratch.rustybuzz_buffer.take().unwrap_or_default();
|
let mut buffer = scratch.rustybuzz_buffer.take().unwrap_or_default();
|
||||||
buffer.set_direction(if span_rtl {
|
buffer.set_direction(if span_rtl {
|
||||||
|
|
@ -197,7 +200,7 @@ fn shape_fallback(
|
||||||
color_opt: attrs.color_opt,
|
color_opt: attrs.color_opt,
|
||||||
metadata: attrs.metadata,
|
metadata: attrs.metadata,
|
||||||
cache_key_flags: attrs.cache_key_flags,
|
cache_key_flags: attrs.cache_key_flags,
|
||||||
metrics_opt: attrs.metrics_opt.map(|x| x.into()),
|
metrics_opt: attrs.metrics_opt.map(Into::into),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -286,9 +289,8 @@ fn shape_run(
|
||||||
|
|
||||||
//TODO: improve performance!
|
//TODO: improve performance!
|
||||||
while !missing.is_empty() {
|
while !missing.is_empty() {
|
||||||
let font = match font_iter.next() {
|
let Some(font) = font_iter.next() else {
|
||||||
Some(some) => some,
|
break;
|
||||||
None => break,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
log::trace!(
|
log::trace!(
|
||||||
|
|
@ -335,9 +337,8 @@ fn shape_run(
|
||||||
while i < glyphs.len() {
|
while i < glyphs.len() {
|
||||||
if glyphs[i].start >= start && glyphs[i].end <= end {
|
if glyphs[i].start >= start && glyphs[i].end <= end {
|
||||||
break;
|
break;
|
||||||
} else {
|
|
||||||
i += 1;
|
|
||||||
}
|
}
|
||||||
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove prior glyphs
|
// Remove prior glyphs
|
||||||
|
|
@ -495,7 +496,7 @@ fn shape_skip(
|
||||||
color_opt: attrs.color_opt,
|
color_opt: attrs.color_opt,
|
||||||
metadata: attrs.metadata,
|
metadata: attrs.metadata,
|
||||||
cache_key_flags: attrs.cache_key_flags,
|
cache_key_flags: attrs.cache_key_flags,
|
||||||
metrics_opt: attrs.metrics_opt.map(|x| x.into()),
|
metrics_opt: attrs.metrics_opt.map(Into::into),
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
@ -523,7 +524,7 @@ pub struct ShapeGlyph {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ShapeGlyph {
|
impl ShapeGlyph {
|
||||||
fn layout(
|
const fn layout(
|
||||||
&self,
|
&self,
|
||||||
font_size: f32,
|
font_size: f32,
|
||||||
line_height_opt: Option<f32>,
|
line_height_opt: Option<f32>,
|
||||||
|
|
@ -633,7 +634,7 @@ impl ShapeWord {
|
||||||
word.is_ascii() && !word.chars().any(|c| c.is_ascii_control() && c != '\t');
|
word.is_ascii() && !word.chars().any(|c| c.is_ascii_control() && c != '\t');
|
||||||
|
|
||||||
if is_simple_ascii && !word.is_empty() {
|
if is_simple_ascii && !word.is_empty() {
|
||||||
let attrs = attrs_list.defaults();
|
let _attrs = attrs_list.defaults();
|
||||||
shaping.run(
|
shaping.run(
|
||||||
&mut glyphs,
|
&mut glyphs,
|
||||||
font_system,
|
font_system,
|
||||||
|
|
@ -685,7 +686,7 @@ impl ShapeWord {
|
||||||
/// Get the width of the [`ShapeWord`] in pixels, using the [`ShapeGlyph::width`] function.
|
/// Get the width of the [`ShapeWord`] in pixels, using the [`ShapeGlyph::width`] function.
|
||||||
pub fn width(&self, font_size: f32) -> f32 {
|
pub fn width(&self, font_size: f32) -> f32 {
|
||||||
let mut width = 0.0;
|
let mut width = 0.0;
|
||||||
for glyph in self.glyphs.iter() {
|
for glyph in &self.glyphs {
|
||||||
width += glyph.width(font_size);
|
width += glyph.width(font_size);
|
||||||
}
|
}
|
||||||
width
|
width
|
||||||
|
|
@ -920,7 +921,7 @@ impl ShapeLine {
|
||||||
|
|
||||||
log::trace!("Line {}: '{}'", if rtl { "RTL" } else { "LTR" }, line);
|
log::trace!("Line {}: '{}'", if rtl { "RTL" } else { "LTR" }, line);
|
||||||
|
|
||||||
for para_info in bidi.paragraphs.iter() {
|
for para_info in &bidi.paragraphs {
|
||||||
let line_rtl = para_info.level.is_rtl();
|
let line_rtl = para_info.level.is_rtl();
|
||||||
assert_eq!(line_rtl, rtl);
|
assert_eq!(line_rtl, rtl);
|
||||||
|
|
||||||
|
|
@ -971,12 +972,12 @@ impl ShapeLine {
|
||||||
|
|
||||||
// Adjust for tabs
|
// Adjust for tabs
|
||||||
let mut x = 0.0;
|
let mut x = 0.0;
|
||||||
for span in spans.iter_mut() {
|
for span in &mut spans {
|
||||||
for word in span.words.iter_mut() {
|
for word in &mut span.words {
|
||||||
for glyph in word.glyphs.iter_mut() {
|
for glyph in &mut word.glyphs {
|
||||||
if line.get(glyph.start..glyph.end) == Some("\t") {
|
if line.get(glyph.start..glyph.end) == Some("\t") {
|
||||||
// Tabs are shaped as spaces, so they will always have the x_advance of a space.
|
// Tabs are shaped as spaces, so they will always have the x_advance of a space.
|
||||||
let tab_x_advance = (tab_width as f32) * glyph.x_advance;
|
let tab_x_advance = f32::from(tab_width) * glyph.x_advance;
|
||||||
let tab_stop = (math::floorf(x / tab_x_advance) + 1.0) * tab_x_advance;
|
let tab_stop = (math::floorf(x / tab_x_advance) + 1.0) * tab_x_advance;
|
||||||
glyph.x_advance = tab_stop - x;
|
glyph.x_advance = tab_stop - x;
|
||||||
}
|
}
|
||||||
|
|
@ -987,7 +988,7 @@ impl ShapeLine {
|
||||||
|
|
||||||
self.rtl = rtl;
|
self.rtl = rtl;
|
||||||
self.spans = spans;
|
self.spans = spans;
|
||||||
self.metrics_opt = attrs_list.defaults().metrics_opt.map(|x| x.into());
|
self.metrics_opt = attrs_list.defaults().metrics_opt.map(Into::into);
|
||||||
|
|
||||||
// Return the buffer for later reuse.
|
// Return the buffer for later reuse.
|
||||||
font_system.shape_buffer.spans = cached_spans;
|
font_system.shape_buffer.spans = cached_spans;
|
||||||
|
|
@ -995,7 +996,7 @@ impl ShapeLine {
|
||||||
|
|
||||||
// A modified version of first part of unicode_bidi::bidi_info::visual_run
|
// A modified version of first part of unicode_bidi::bidi_info::visual_run
|
||||||
fn adjust_levels(para: &unicode_bidi::Paragraph) -> Vec<unicode_bidi::Level> {
|
fn adjust_levels(para: &unicode_bidi::Paragraph) -> Vec<unicode_bidi::Level> {
|
||||||
use unicode_bidi::BidiClass::*;
|
use unicode_bidi::BidiClass::{B, BN, FSI, LRE, LRI, LRO, PDF, PDI, RLE, RLI, RLO, S, WS};
|
||||||
let text = para.info.text;
|
let text = para.info.text;
|
||||||
let levels = ¶.info.levels;
|
let levels = ¶.info.levels;
|
||||||
let original_classes = ¶.info.original_classes;
|
let original_classes = ¶.info.original_classes;
|
||||||
|
|
@ -1141,6 +1142,23 @@ impl ShapeLine {
|
||||||
layout_lines: &mut Vec<LayoutLine>,
|
layout_lines: &mut Vec<LayoutLine>,
|
||||||
match_mono_width: Option<f32>,
|
match_mono_width: Option<f32>,
|
||||||
) {
|
) {
|
||||||
|
fn add_to_visual_line(
|
||||||
|
vl: &mut VisualLine,
|
||||||
|
span_index: usize,
|
||||||
|
start: (usize, usize),
|
||||||
|
end: (usize, usize),
|
||||||
|
width: f32,
|
||||||
|
number_of_blanks: u32,
|
||||||
|
) {
|
||||||
|
if end == start {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vl.ranges.push((span_index, start, end));
|
||||||
|
vl.w += width;
|
||||||
|
vl.spaces += number_of_blanks;
|
||||||
|
}
|
||||||
|
|
||||||
// For each visual line a list of (span index, and range of words in that span)
|
// For each visual line a list of (span index, and range of words in that span)
|
||||||
// Note that a BiDi visual line could have multiple spans or parts of them
|
// Note that a BiDi visual line could have multiple spans or parts of them
|
||||||
// let mut vl_range_of_spans = Vec::with_capacity(1);
|
// let mut vl_range_of_spans = Vec::with_capacity(1);
|
||||||
|
|
@ -1160,23 +1178,6 @@ impl ShapeLine {
|
||||||
v.glyphs
|
v.glyphs
|
||||||
}));
|
}));
|
||||||
|
|
||||||
fn add_to_visual_line(
|
|
||||||
vl: &mut VisualLine,
|
|
||||||
span_index: usize,
|
|
||||||
start: (usize, usize),
|
|
||||||
end: (usize, usize),
|
|
||||||
width: f32,
|
|
||||||
number_of_blanks: u32,
|
|
||||||
) {
|
|
||||||
if end == start {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
vl.ranges.push((span_index, start, end));
|
|
||||||
vl.w += width;
|
|
||||||
vl.spaces += number_of_blanks;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This would keep the maximum number of spans that would fit on a visual line
|
// This would keep the maximum number of spans that would fit on a visual line
|
||||||
// If one span is too large, this variable will hold the range of words inside that span
|
// If one span is too large, this variable will hold the range of words inside that span
|
||||||
// that fits on a line.
|
// that fits on a line.
|
||||||
|
|
@ -1187,7 +1188,7 @@ impl ShapeLine {
|
||||||
for (span_index, span) in self.spans.iter().enumerate() {
|
for (span_index, span) in self.spans.iter().enumerate() {
|
||||||
let mut word_range_width = 0.;
|
let mut word_range_width = 0.;
|
||||||
let mut number_of_blanks: u32 = 0;
|
let mut number_of_blanks: u32 = 0;
|
||||||
for word in span.words.iter() {
|
for word in &span.words {
|
||||||
let word_width = word.width(font_size);
|
let word_width = word.width(font_size);
|
||||||
word_range_width += word_width;
|
word_range_width += word_width;
|
||||||
if word.blank {
|
if word.blank {
|
||||||
|
|
@ -1232,7 +1233,6 @@ impl ShapeLine {
|
||||||
width_before_last_blank = word_range_width;
|
width_before_last_blank = word_range_width;
|
||||||
}
|
}
|
||||||
word_range_width += word_width;
|
word_range_width += word_width;
|
||||||
continue;
|
|
||||||
} else if wrap == Wrap::Glyph
|
} else if wrap == Wrap::Glyph
|
||||||
// Make sure that the word is able to fit on it's own line, if not, fall back to Glyph wrapping.
|
// Make sure that the word is able to fit on it's own line, if not, fall back to Glyph wrapping.
|
||||||
|| (wrap == Wrap::WordOrGlyph && word_width > width_opt.unwrap_or(f32::INFINITY))
|
|| (wrap == Wrap::WordOrGlyph && word_width > width_opt.unwrap_or(f32::INFINITY))
|
||||||
|
|
@ -1266,7 +1266,6 @@ impl ShapeLine {
|
||||||
<= width_opt.unwrap_or(f32::INFINITY)
|
<= width_opt.unwrap_or(f32::INFINITY)
|
||||||
{
|
{
|
||||||
word_range_width += glyph_width;
|
word_range_width += glyph_width;
|
||||||
continue;
|
|
||||||
} else {
|
} else {
|
||||||
add_to_visual_line(
|
add_to_visual_line(
|
||||||
&mut current_visual_line,
|
&mut current_visual_line,
|
||||||
|
|
@ -1358,7 +1357,6 @@ impl ShapeLine {
|
||||||
width_before_last_blank = word_range_width;
|
width_before_last_blank = word_range_width;
|
||||||
}
|
}
|
||||||
word_range_width += word_width;
|
word_range_width += word_width;
|
||||||
continue;
|
|
||||||
} else if wrap == Wrap::Glyph
|
} else if wrap == Wrap::Glyph
|
||||||
// Make sure that the word is able to fit on it's own line, if not, fall back to Glyph wrapping.
|
// Make sure that the word is able to fit on it's own line, if not, fall back to Glyph wrapping.
|
||||||
|| (wrap == Wrap::WordOrGlyph && word_width > width_opt.unwrap_or(f32::INFINITY))
|
|| (wrap == Wrap::WordOrGlyph && word_width > width_opt.unwrap_or(f32::INFINITY))
|
||||||
|
|
@ -1392,7 +1390,6 @@ impl ShapeLine {
|
||||||
<= width_opt.unwrap_or(f32::INFINITY)
|
<= width_opt.unwrap_or(f32::INFINITY)
|
||||||
{
|
{
|
||||||
word_range_width += glyph_width;
|
word_range_width += glyph_width;
|
||||||
continue;
|
|
||||||
} else {
|
} else {
|
||||||
add_to_visual_line(
|
add_to_visual_line(
|
||||||
&mut current_visual_line,
|
&mut current_visual_line,
|
||||||
|
|
@ -1467,26 +1464,26 @@ impl ShapeLine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !current_visual_line.ranges.is_empty() {
|
if current_visual_line.ranges.is_empty() {
|
||||||
visual_lines.push(current_visual_line);
|
|
||||||
} else {
|
|
||||||
current_visual_line.clear();
|
current_visual_line.clear();
|
||||||
cached_visual_lines.push(current_visual_line);
|
cached_visual_lines.push(current_visual_line);
|
||||||
|
} else {
|
||||||
|
visual_lines.push(current_visual_line);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the LayoutLines using the ranges inside visual lines
|
// Create the LayoutLines using the ranges inside visual lines
|
||||||
let align = align.unwrap_or(if self.rtl { Align::Right } else { Align::Left });
|
let align = align.unwrap_or(if self.rtl { Align::Right } else { Align::Left });
|
||||||
|
|
||||||
let line_width = match width_opt {
|
let line_width = width_opt.map_or_else(
|
||||||
Some(width) => width,
|
|| {
|
||||||
None => {
|
|
||||||
let mut width: f32 = 0.0;
|
let mut width: f32 = 0.0;
|
||||||
for visual_line in visual_lines.iter() {
|
for visual_line in &visual_lines {
|
||||||
width = width.max(visual_line.w);
|
width = width.max(visual_line.w);
|
||||||
}
|
}
|
||||||
width
|
width
|
||||||
}
|
},
|
||||||
};
|
|width| width,
|
||||||
|
);
|
||||||
|
|
||||||
let start_x = if self.rtl { line_width } else { 0.0 };
|
let start_x = if self.rtl { line_width } else { 0.0 };
|
||||||
|
|
||||||
|
|
@ -1546,7 +1543,7 @@ impl ShapeLine {
|
||||||
|
|
||||||
let mut process_range = |range: Range<usize>| {
|
let mut process_range = |range: Range<usize>| {
|
||||||
for &(span_index, (starting_word, starting_glyph), (ending_word, ending_glyph)) in
|
for &(span_index, (starting_word, starting_glyph), (ending_word, ending_glyph)) in
|
||||||
visual_line.ranges[range.clone()].iter()
|
&visual_line.ranges[range]
|
||||||
{
|
{
|
||||||
let span = &self.spans[span_index];
|
let span = &self.spans[span_index];
|
||||||
// If ending_glyph is not 0 we need to include glyphs from the ending_word
|
// If ending_glyph is not 0 we need to include glyphs from the ending_word
|
||||||
|
|
@ -1585,12 +1582,14 @@ impl ShapeLine {
|
||||||
_ => font_size,
|
_ => font_size,
|
||||||
};
|
};
|
||||||
|
|
||||||
let x_advance = glyph_font_size * glyph.x_advance
|
let x_advance = glyph_font_size.mul_add(
|
||||||
+ if word.blank {
|
glyph.x_advance,
|
||||||
|
if word.blank {
|
||||||
justification_expansion
|
justification_expansion
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0
|
||||||
};
|
},
|
||||||
|
);
|
||||||
if self.rtl {
|
if self.rtl {
|
||||||
x -= x_advance;
|
x -= x_advance;
|
||||||
}
|
}
|
||||||
|
|
@ -1626,12 +1625,12 @@ impl ShapeLine {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut line_height_opt: Option<f32> = None;
|
let mut line_height_opt: Option<f32> = None;
|
||||||
for glyph in glyphs.iter() {
|
for glyph in &glyphs {
|
||||||
if let Some(glyph_line_height) = glyph.line_height_opt {
|
if let Some(glyph_line_height) = glyph.line_height_opt {
|
||||||
line_height_opt = match line_height_opt {
|
line_height_opt = line_height_opt
|
||||||
Some(line_height) => Some(line_height.max(glyph_line_height)),
|
.map_or(Some(glyph_line_height), |line_height| {
|
||||||
None => Some(glyph_line_height),
|
Some(line_height.max(glyph_line_height))
|
||||||
};
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1657,7 +1656,7 @@ impl ShapeLine {
|
||||||
max_ascent: 0.0,
|
max_ascent: 0.0,
|
||||||
max_descent: 0.0,
|
max_descent: 0.0,
|
||||||
line_height_opt: self.metrics_opt.map(|x| x.line_height),
|
line_height_opt: self.metrics_opt.map(|x| x.line_height),
|
||||||
glyphs: Default::default(),
|
glyphs: Vec::default(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
22
src/swash.rs
22
src/swash.rs
|
|
@ -17,12 +17,9 @@ fn swash_image(
|
||||||
context: &mut ScaleContext,
|
context: &mut ScaleContext,
|
||||||
cache_key: CacheKey,
|
cache_key: CacheKey,
|
||||||
) -> Option<SwashImage> {
|
) -> Option<SwashImage> {
|
||||||
let font = match font_system.get_font(cache_key.font_id, cache_key.font_weight) {
|
let Some(font) = font_system.get_font(cache_key.font_id, cache_key.font_weight) else {
|
||||||
Some(some) => some,
|
log::warn!("did not find font {:?}", cache_key.font_id);
|
||||||
None => {
|
return None;
|
||||||
log::warn!("did not find font {:?}", cache_key.font_id);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let variable_width = font
|
let variable_width = font
|
||||||
|
|
@ -38,7 +35,7 @@ fn swash_image(
|
||||||
if let Some(variation) = variable_width {
|
if let Some(variation) = variable_width {
|
||||||
scaler = scaler.variations(std::iter::once(swash::Setting {
|
scaler = scaler.variations(std::iter::once(swash::Setting {
|
||||||
tag: swash::Tag::from_be_bytes(*b"wght"),
|
tag: swash::Tag::from_be_bytes(*b"wght"),
|
||||||
value: (cache_key.font_weight.0 as f32)
|
value: f32::from(cache_key.font_weight.0)
|
||||||
.clamp(variation.min_value(), variation.max_value()),
|
.clamp(variation.min_value(), variation.max_value()),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
@ -87,12 +84,9 @@ fn swash_outline_commands(
|
||||||
) -> Option<Box<[swash::zeno::Command]>> {
|
) -> Option<Box<[swash::zeno::Command]>> {
|
||||||
use swash::zeno::PathData as _;
|
use swash::zeno::PathData as _;
|
||||||
|
|
||||||
let font = match font_system.get_font(cache_key.font_id, cache_key.font_weight) {
|
let Some(font) = font_system.get_font(cache_key.font_id, cache_key.font_weight) else {
|
||||||
Some(some) => some,
|
log::warn!("did not find font {:?}", cache_key.font_id);
|
||||||
None => {
|
return None;
|
||||||
log::warn!("did not find font {:?}", cache_key.font_id);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Build the scaler
|
// Build the scaler
|
||||||
|
|
@ -206,7 +200,7 @@ impl SwashCache {
|
||||||
f(
|
f(
|
||||||
x + off_x,
|
x + off_x,
|
||||||
y + off_y,
|
y + off_y,
|
||||||
Color(((image.data[i] as u32) << 24) | base.0 & 0xFF_FF_FF),
|
Color((u32::from(image.data[i]) << 24) | base.0 & 0xFF_FF_FF),
|
||||||
);
|
);
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue