Add byte information to PositionedGlyph (#17900)

# Objective

Adds information about the byte length and index of a glyph to
`PositionedGlyph`. Useful for text picking, allows for picking with
multi-byte characters. Also adds a `line` field that helps with
converting back and forth from cosmic's `Cursor`.

## Solution

Retrieve the relevant data from cosmic and add it to the glyph in the
text pipeline.

## Testing

`cargo r -p ci`

---

## Migration Guide

`PositionedGlyph::new()` has been removed as there is no longer an
unused field. Create new `PositionedGlyph`s directly.
This commit is contained in:
sam edelsten 2025-03-08 16:35:01 +00:00 committed by GitHub
parent ab38b61001
commit 64d57fad08
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 75 additions and 80 deletions

View File

@ -20,24 +20,12 @@ pub struct PositionedGlyph {
pub atlas_info: GlyphAtlasInfo, pub atlas_info: GlyphAtlasInfo,
/// The index of the glyph in the [`ComputedTextBlock`](crate::ComputedTextBlock)'s tracked spans. /// The index of the glyph in the [`ComputedTextBlock`](crate::ComputedTextBlock)'s tracked spans.
pub span_index: usize, pub span_index: usize,
/// TODO: In order to do text editing, we need access to the size of glyphs and their index in the associated String. /// The index of the glyph's line.
/// For example, to figure out where to place the cursor in an input box from the mouse's position. pub line_index: usize,
/// Without this, it's only possible in texts where each glyph is one byte. Cosmic text has methods for this /// The byte index of the glyph in it's line.
/// cosmic-texts [hit detection](https://pop-os.github.io/cosmic-text/cosmic_text/struct.Buffer.html#method.hit) pub byte_index: usize,
byte_index: usize, /// The byte length of the glyph.
} pub byte_length: usize,
impl PositionedGlyph {
/// Creates a new [`PositionedGlyph`]
pub fn new(position: Vec2, size: Vec2, atlas_info: GlyphAtlasInfo, span_index: usize) -> Self {
Self {
position,
size,
atlas_info,
span_index,
byte_index: 0,
}
}
} }
/// Information about a glyph in an atlas. /// Information about a glyph in an atlas.

View File

@ -263,77 +263,84 @@ impl TextPipeline {
let buffer = &mut computed.buffer; let buffer = &mut computed.buffer;
let box_size = buffer_dimensions(buffer); let box_size = buffer_dimensions(buffer);
let result = buffer let result = buffer.layout_runs().try_for_each(|run| {
.layout_runs() let result = run
.flat_map(|run| { .glyphs
run.glyphs .iter()
.iter() .map(move |layout_glyph| (layout_glyph, run.line_y, run.line_i))
.map(move |layout_glyph| (layout_glyph, run.line_y)) .try_for_each(|(layout_glyph, line_y, line_i)| {
}) let mut temp_glyph;
.try_for_each(|(layout_glyph, line_y)| { let span_index = layout_glyph.metadata;
let mut temp_glyph; let font_id = glyph_info[span_index].0;
let span_index = layout_glyph.metadata; let font_smoothing = glyph_info[span_index].1;
let font_id = glyph_info[span_index].0;
let font_smoothing = glyph_info[span_index].1;
let layout_glyph = if font_smoothing == FontSmoothing::None { let layout_glyph = if font_smoothing == FontSmoothing::None {
// If font smoothing is disabled, round the glyph positions and sizes, // If font smoothing is disabled, round the glyph positions and sizes,
// effectively discarding all subpixel layout. // effectively discarding all subpixel layout.
temp_glyph = layout_glyph.clone(); temp_glyph = layout_glyph.clone();
temp_glyph.x = temp_glyph.x.round(); temp_glyph.x = temp_glyph.x.round();
temp_glyph.y = temp_glyph.y.round(); temp_glyph.y = temp_glyph.y.round();
temp_glyph.w = temp_glyph.w.round(); temp_glyph.w = temp_glyph.w.round();
temp_glyph.x_offset = temp_glyph.x_offset.round(); temp_glyph.x_offset = temp_glyph.x_offset.round();
temp_glyph.y_offset = temp_glyph.y_offset.round(); temp_glyph.y_offset = temp_glyph.y_offset.round();
temp_glyph.line_height_opt = temp_glyph.line_height_opt.map(f32::round); temp_glyph.line_height_opt = temp_glyph.line_height_opt.map(f32::round);
&temp_glyph &temp_glyph
} else { } else {
layout_glyph layout_glyph
}; };
let font_atlas_set = font_atlas_sets.sets.entry(font_id).or_default(); let font_atlas_set = font_atlas_sets.sets.entry(font_id).or_default();
let physical_glyph = layout_glyph.physical((0., 0.), 1.); let physical_glyph = layout_glyph.physical((0., 0.), 1.);
let atlas_info = font_atlas_set let atlas_info = font_atlas_set
.get_glyph_atlas_info(physical_glyph.cache_key, font_smoothing) .get_glyph_atlas_info(physical_glyph.cache_key, font_smoothing)
.map(Ok) .map(Ok)
.unwrap_or_else(|| { .unwrap_or_else(|| {
font_atlas_set.add_glyph_to_atlas( font_atlas_set.add_glyph_to_atlas(
texture_atlases, texture_atlases,
textures, textures,
&mut font_system.0, &mut font_system.0,
&mut swash_cache.0, &mut swash_cache.0,
layout_glyph, layout_glyph,
font_smoothing, font_smoothing,
) )
})?; })?;
let texture_atlas = texture_atlases.get(&atlas_info.texture_atlas).unwrap(); let texture_atlas = texture_atlases.get(&atlas_info.texture_atlas).unwrap();
let location = atlas_info.location; let location = atlas_info.location;
let glyph_rect = texture_atlas.textures[location.glyph_index]; let glyph_rect = texture_atlas.textures[location.glyph_index];
let left = location.offset.x as f32; let left = location.offset.x as f32;
let top = location.offset.y as f32; let top = location.offset.y as f32;
let glyph_size = UVec2::new(glyph_rect.width(), glyph_rect.height()); let glyph_size = UVec2::new(glyph_rect.width(), glyph_rect.height());
// offset by half the size because the origin is center // offset by half the size because the origin is center
let x = glyph_size.x as f32 / 2.0 + left + physical_glyph.x as f32; let x = glyph_size.x as f32 / 2.0 + left + physical_glyph.x as f32;
let y = line_y.round() + physical_glyph.y as f32 - top + glyph_size.y as f32 / 2.0; let y =
let y = match y_axis_orientation { line_y.round() + physical_glyph.y as f32 - top + glyph_size.y as f32 / 2.0;
YAxisOrientation::TopToBottom => y, let y = match y_axis_orientation {
YAxisOrientation::BottomToTop => box_size.y - y, YAxisOrientation::TopToBottom => y,
}; YAxisOrientation::BottomToTop => box_size.y - y,
};
let position = Vec2::new(x, y); let position = Vec2::new(x, y);
// TODO: recreate the byte index, that keeps track of where a cursor is, let pos_glyph = PositionedGlyph {
// when glyphs are not limited to single byte representation, relevant for #1319 position,
let pos_glyph = size: glyph_size.as_vec2(),
PositionedGlyph::new(position, glyph_size.as_vec2(), atlas_info, span_index); atlas_info,
layout_info.glyphs.push(pos_glyph); span_index,
Ok(()) byte_index: layout_glyph.start,
}); byte_length: layout_glyph.end - layout_glyph.start,
line_index: line_i,
};
layout_info.glyphs.push(pos_glyph);
Ok(())
});
result
});
// Return the scratch vec. // Return the scratch vec.
self.glyph_info = glyph_info; self.glyph_info = glyph_info;