🔧 Rename textleafs and inlines to keep coherence between render and editor

This commit is contained in:
Aitor Moreno
2025-10-22 11:33:03 +02:00
parent 140290cd60
commit eb088c31c1
26 changed files with 665 additions and 657 deletions

View File

@@ -33,8 +33,8 @@ pub fn stroke_paragraph_builder_group_from_text(
let mut stroke_paragraphs_map: std::collections::HashMap<usize, ParagraphBuilder> =
std::collections::HashMap::new();
for leaf in paragraph.children().iter() {
let text_paint: skia_safe::Handle<_> = merge_fills(leaf.fills(), *bounds);
for span in paragraph.children().iter() {
let text_paint: skia_safe::Handle<_> = merge_fills(span.fills(), *bounds);
let stroke_paints = get_text_stroke_paints(
stroke,
bounds,
@@ -43,7 +43,7 @@ pub fn stroke_paragraph_builder_group_from_text(
remove_stroke_alpha,
);
let text: String = leaf.apply_text_transform();
let text: String = span.apply_text_transform();
for (paint_idx, stroke_paint) in stroke_paints.iter().enumerate() {
let builder = stroke_paragraphs_map.entry(paint_idx).or_insert_with(|| {
@@ -51,9 +51,9 @@ pub fn stroke_paragraph_builder_group_from_text(
ParagraphBuilder::new(&paragraph_style, fonts)
});
let stroke_paint = stroke_paint.clone();
let remove_alpha = use_shadow.unwrap_or(false) && !leaf.is_transparent();
let remove_alpha = use_shadow.unwrap_or(false) && !span.is_transparent();
let stroke_style =
leaf.to_stroke_style(&stroke_paint, fallback_fonts, remove_alpha);
span.to_stroke_style(&stroke_paint, fallback_fonts, remove_alpha);
builder.push_style(&stroke_style);
builder.add_text(&text);
}
@@ -342,7 +342,7 @@ fn render_text_decoration(
let (max_underline_thickness, underline_y, max_strike_thickness, strike_y) =
calculate_decoration_metrics(&style_metrics, line_baseline);
// Draw decorations per segment (text leaf)
// Draw decorations per segment (text span)
for (i, (style_start, style_metric)) in style_metrics.iter().enumerate() {
let text_style = &style_metric.text_style;
let style_end = style_metrics

View File

@@ -98,7 +98,7 @@ impl TextContentSize {
pub struct TextPositionWithAffinity {
pub position_with_affinity: PositionWithAffinity,
pub paragraph: i32,
pub leaf: i32,
pub span: i32,
pub offset: i32,
}
@@ -106,13 +106,13 @@ impl TextPositionWithAffinity {
pub fn new(
position_with_affinity: PositionWithAffinity,
paragraph: i32,
leaf: i32,
span: i32,
offset: i32,
) -> Self {
Self {
position_with_affinity,
paragraph,
leaf,
span,
offset,
}
}
@@ -289,7 +289,7 @@ impl TextContent {
let layout_paragraphs = self.layout.paragraphs.iter().flatten();
let mut paragraph_index: i32 = -1;
let mut leaf_index: i32 = -1;
let mut span_index: i32 = -1;
for layout_paragraph in layout_paragraphs {
paragraph_index += 1;
let start_y = offset_y;
@@ -303,17 +303,17 @@ impl TextContent {
if let Some(paragraph) = self.paragraphs().get(paragraph_index as usize) {
// Computed position keeps the current position in terms
// of number of characters of text. This is used to know
// in which leaf we are.
// in which span we are.
let mut computed_position = 0;
let mut leaf_offset = 0;
for leaf in paragraph.children() {
leaf_index += 1;
let length = leaf.text.len();
let mut span_offset = 0;
for span in paragraph.children() {
span_index += 1;
let length = span.text.len();
let start_position = computed_position;
let end_position = computed_position + length;
let current_position = position_with_affinity.position as usize;
if start_position <= current_position && end_position >= current_position {
leaf_offset = position_with_affinity.position - start_position as i32;
span_offset = position_with_affinity.position - start_position as i32;
break;
}
computed_position += length;
@@ -321,8 +321,8 @@ impl TextContent {
return Some(TextPositionWithAffinity::new(
position_with_affinity,
paragraph_index,
leaf_index,
leaf_offset,
span_index,
span_offset,
));
}
}
@@ -344,10 +344,10 @@ impl TextContent {
for paragraph in self.paragraphs() {
let paragraph_style = paragraph.paragraph_to_style();
let mut builder = ParagraphBuilder::new(&paragraph_style, fonts);
for leaf in paragraph.children() {
let remove_alpha = use_shadow.unwrap_or(false) && !leaf.is_transparent();
let text_style = leaf.to_style(&self.bounds(), fallback_fonts, remove_alpha);
let text = leaf.apply_text_transform();
for span in paragraph.children() {
let remove_alpha = use_shadow.unwrap_or(false) && !span.is_transparent();
let text_style = span.to_style(&self.bounds(), fallback_fonts, remove_alpha);
let text = span.apply_text_transform();
builder.push_style(&text_style);
builder.add_text(&text);
}
@@ -519,7 +519,7 @@ pub struct Paragraph {
letter_spacing: f32,
typography_ref_file: Uuid,
typography_ref_id: Uuid,
children: Vec<TextLeaf>,
children: Vec<TextSpan>,
}
impl Default for Paragraph {
@@ -549,7 +549,7 @@ impl Paragraph {
letter_spacing: f32,
typography_ref_file: Uuid,
typography_ref_id: Uuid,
children: Vec<TextLeaf>,
children: Vec<TextSpan>,
) -> Self {
Self {
text_align,
@@ -565,17 +565,17 @@ impl Paragraph {
}
#[allow(dead_code)]
fn set_children(&mut self, children: Vec<TextLeaf>) {
fn set_children(&mut self, children: Vec<TextSpan>) {
self.children = children;
}
pub fn children(&self) -> &[TextLeaf] {
pub fn children(&self) -> &[TextSpan] {
&self.children
}
#[allow(dead_code)]
fn add_leaf(&mut self, leaf: TextLeaf) {
self.children.push(leaf);
fn add_span(&mut self, span: TextSpan) {
self.children.push(span);
}
// FIXME: move serialization to wasm module
@@ -622,7 +622,7 @@ impl Paragraph {
}
#[derive(Debug, PartialEq, Clone)]
pub struct TextLeaf {
pub struct TextSpan {
text: String,
font_family: FontFamily,
font_size: f32,
@@ -635,7 +635,7 @@ pub struct TextLeaf {
fills: Vec<shapes::Fill>,
}
impl TextLeaf {
impl TextSpan {
#[allow(clippy::too_many_arguments)]
pub fn new(
text: String,

View File

@@ -37,7 +37,7 @@ impl TextPaths {
let start = line_metrics.start_index;
let end = line_metrics.end_index;
// 3. Get styles present in line for each text leaf
// 3. Get styles present in line for each text span
let style_metrics = line_metrics.get_style_metrics(start..end);
let mut offset_x = 0.0;
@@ -56,23 +56,23 @@ impl TextPaths {
.map(|(i, _)| i)
.unwrap_or(text.len());
let leaf_text = &text[start_byte..end_byte];
let span_text = &text[start_byte..end_byte];
let font = skia_paragraph.get_font_at(*start_index);
let blob_offset_x = self.bounds.x() + line_metrics.left as f32 + offset_x;
let blob_offset_y = line_offset_y;
// 4. Get the path for each text leaf
// 4. Get the path for each text span
if let Some((text_path, paint)) = self.generate_text_path(
leaf_text,
span_text,
&font,
blob_offset_x,
blob_offset_y,
style_metric,
antialias,
) {
let text_width = font.measure_text(leaf_text, None).0;
let text_width = font.measure_text(span_text, None).0;
offset_x += text_width;
paths.push((text_path, paint));
}
@@ -87,7 +87,7 @@ impl TextPaths {
fn generate_text_path(
&self,
leaf_text: &str,
span_text: &str,
font: &skia::Font,
blob_offset_x: f32,
blob_offset_y: f32,
@@ -99,10 +99,10 @@ impl TextPaths {
// This is used to avoid rendering empty paths, but we can
// revisit this logic later
if let Some((text_blob_path, text_blob_bounds)) =
Self::get_text_blob_path(leaf_text, font, blob_offset_x, blob_offset_y)
Self::get_text_blob_path(span_text, font, blob_offset_x, blob_offset_y)
{
let mut text_path = text_blob_path.clone();
let text_width = font.measure_text(leaf_text, None).0;
let text_width = font.measure_text(span_text, None).0;
let decoration = style_metric.text_style.decoration();
let font_metrics = style_metric.font_metrics;
@@ -165,13 +165,13 @@ impl TextPaths {
}
fn get_text_blob_path(
leaf_text: &str,
span_text: &str,
font: &skia::Font,
blob_offset_x: f32,
blob_offset_y: f32,
) -> Option<(skia::Path, skia::Rect)> {
with_state_mut!(state, {
let utf16_text = leaf_text.encode_utf16().collect::<Vec<u16>>();
let utf16_text = span_text.encode_utf16().collect::<Vec<u16>>();
let text = unsafe { skia_safe::as_utf16_unchecked(&utf16_text) };
let emoji_font = state.render_state.fonts().get_emoji_font(font.size());
let use_font = emoji_font.as_ref().unwrap_or(font);

View File

@@ -3,21 +3,21 @@
use crate::shapes::TextPositionWithAffinity;
/// TODO: Now this is just a tuple with 2 i32 working
/// as indices (paragraph and leaf).
/// as indices (paragraph and span).
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct TextNodePosition {
pub paragraph: i32,
pub leaf: i32,
pub span: i32,
}
impl TextNodePosition {
pub fn new(paragraph: i32, leaf: i32) -> Self {
Self { paragraph, leaf }
pub fn new(paragraph: i32, span: i32) -> Self {
Self { paragraph, span }
}
#[allow(dead_code)]
pub fn is_invalid(&self) -> bool {
self.paragraph < 0 || self.leaf < 0
self.paragraph < 0 || self.span < 0
}
}
@@ -95,7 +95,7 @@ impl TextEditorState {
self.selection.set(
Some(TextNodePosition::new(
text_position_with_affinity.paragraph,
text_position_with_affinity.leaf,
text_position_with_affinity.span,
)),
text_position_with_affinity.offset,
);

View File

@@ -9,7 +9,7 @@ use crate::shapes::{
use crate::utils::{uuid_from_u32, uuid_from_u32_quartet};
use crate::{with_current_shape_mut, with_state_mut, with_state_mut_current_shape, STATE};
const RAW_LEAF_DATA_SIZE: usize = std::mem::size_of::<RawTextLeaf>();
const RAW_SPAN_DATA_SIZE: usize = std::mem::size_of::<RawTextSpan>();
const RAW_PARAGRAPH_DATA_SIZE: usize = std::mem::size_of::<RawParagraphData>();
const MAX_TEXT_FILLS: usize = 8;
@@ -94,7 +94,7 @@ impl From<RawTextTransform> for Option<TextTransform> {
#[repr(align(4))]
#[derive(Debug, Clone, Copy)]
pub struct RawParagraphData {
leaf_count: u32,
span_count: u32,
text_align: RawTextAlign,
text_direction: RawTextDirection,
text_decoration: RawTextDecoration,
@@ -124,7 +124,7 @@ impl TryFrom<&[u8]> for RawParagraphData {
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct RawTextLeaf {
pub struct RawTextSpan {
font_style: RawFontStyle,
text_decoration: RawTextDecoration,
text_transform: RawTextTransform,
@@ -140,25 +140,25 @@ pub struct RawTextLeaf {
fills: [RawFillData; MAX_TEXT_FILLS],
}
impl From<[u8; RAW_LEAF_DATA_SIZE]> for RawTextLeaf {
fn from(bytes: [u8; RAW_LEAF_DATA_SIZE]) -> Self {
impl From<[u8; RAW_SPAN_DATA_SIZE]> for RawTextSpan {
fn from(bytes: [u8; RAW_SPAN_DATA_SIZE]) -> Self {
unsafe { std::mem::transmute(bytes) }
}
}
impl TryFrom<&[u8]> for RawTextLeaf {
impl TryFrom<&[u8]> for RawTextSpan {
type Error = String;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
let data: [u8; RAW_LEAF_DATA_SIZE] = bytes
.get(0..RAW_LEAF_DATA_SIZE)
let data: [u8; RAW_SPAN_DATA_SIZE] = bytes
.get(0..RAW_SPAN_DATA_SIZE)
.and_then(|slice| slice.try_into().ok())
.ok_or("Invalid text leaf data".to_string())?;
Ok(RawTextLeaf::from(data))
.ok_or("Invalid text span data".to_string())?;
Ok(RawTextSpan::from(data))
}
}
impl From<RawTextLeaf> for shapes::TextLeaf {
fn from(value: RawTextLeaf) -> Self {
impl From<RawTextSpan> for shapes::TextSpan {
fn from(value: RawTextSpan) -> Self {
let text = String::default();
let font_family = shapes::FontFamily::new(
@@ -193,7 +193,7 @@ impl From<RawTextLeaf> for shapes::TextLeaf {
#[derive(Debug, Clone)]
pub struct RawParagraph {
attrs: RawParagraphData,
leaves: Vec<RawTextLeaf>,
leaves: Vec<RawTextSpan>,
text_buffer: Vec<u8>,
}
@@ -204,12 +204,12 @@ impl TryFrom<&Vec<u8>> for RawParagraph {
fn try_from(bytes: &Vec<u8>) -> Result<Self, Self::Error> {
let attrs = RawParagraphData::try_from(&bytes[..RAW_PARAGRAPH_DATA_SIZE])?;
let mut offset = RAW_PARAGRAPH_DATA_SIZE;
let mut raw_text_leaves: Vec<RawTextLeaf> = Vec::new();
let mut raw_text_leaves: Vec<RawTextSpan> = Vec::new();
for _ in 0..attrs.leaf_count {
let text_leaf = RawTextLeaf::try_from(&bytes[offset..(offset + RAW_LEAF_DATA_SIZE)])?;
offset += RAW_LEAF_DATA_SIZE;
raw_text_leaves.push(text_leaf);
for _ in 0..attrs.span_count {
let text_span = RawTextSpan::try_from(&bytes[offset..(offset + RAW_SPAN_DATA_SIZE)])?;
offset += RAW_SPAN_DATA_SIZE;
raw_text_leaves.push(text_span);
}
let text_buffer = &bytes[offset..];
@@ -230,16 +230,16 @@ impl From<RawParagraph> for shapes::Paragraph {
let mut leaves = vec![];
let mut offset = 0;
for raw_leaf in value.leaves.into_iter() {
let delta = raw_leaf.text_length as usize;
for raw_span in value.leaves.into_iter() {
let delta = raw_span.text_length as usize;
let text_buffer = &value.text_buffer[offset..offset + delta];
let mut leaf = shapes::TextLeaf::from(raw_leaf);
let mut span = shapes::TextSpan::from(raw_span);
if !text_buffer.is_empty() {
leaf.set_text(String::from_utf8_lossy(text_buffer).to_string());
span.set_text(String::from_utf8_lossy(text_buffer).to_string());
}
leaves.push(leaf);
leaves.push(span);
offset += delta;
}