mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-31 07:37:24 +00:00
textlayout: Add the ability for the line callback to break with a value
This commit is contained in:
parent
7fe4cb39aa
commit
c8c39e82b4
2 changed files with 109 additions and 77 deletions
|
@ -1164,65 +1164,68 @@ impl<'a, T: ProcessScene> SceneBuilder<'a, T> {
|
|||
Font: crate::textlayout::TextShaper<Length = PhysicalLength>,
|
||||
Font: GlyphRenderer,
|
||||
{
|
||||
paragraph.layout_lines(|glyphs, line_x, line_y| {
|
||||
let baseline_y = line_y + paragraph.layout.font.ascent();
|
||||
while let Some(positioned_glyph) = glyphs.next() {
|
||||
let glyph = paragraph.layout.font.render_glyph(positioned_glyph.glyph_id);
|
||||
paragraph
|
||||
.layout_lines::<()>(|glyphs, line_x, line_y| {
|
||||
let baseline_y = line_y + paragraph.layout.font.ascent();
|
||||
while let Some(positioned_glyph) = glyphs.next() {
|
||||
let glyph = paragraph.layout.font.render_glyph(positioned_glyph.glyph_id);
|
||||
|
||||
let src_rect = PhysicalRect::new(
|
||||
PhysicalPoint::from_lengths(
|
||||
line_x + positioned_glyph.x + glyph.x,
|
||||
baseline_y - glyph.y - glyph.height,
|
||||
),
|
||||
glyph.size(),
|
||||
)
|
||||
.cast();
|
||||
let src_rect = PhysicalRect::new(
|
||||
PhysicalPoint::from_lengths(
|
||||
line_x + positioned_glyph.x + glyph.x,
|
||||
baseline_y - glyph.y - glyph.height,
|
||||
),
|
||||
glyph.size(),
|
||||
)
|
||||
.cast();
|
||||
|
||||
if let Some(clipped_src) = src_rect.intersection(&physical_clip) {
|
||||
let geometry = clipped_src.translate(offset).round();
|
||||
let origin = (geometry.origin - offset.round()).cast::<usize>();
|
||||
let actual_x = origin.x - src_rect.origin.x as usize;
|
||||
let actual_y = origin.y - src_rect.origin.y as usize;
|
||||
let stride = glyph.width.get() as u16;
|
||||
let geometry = geometry.cast();
|
||||
if let Some(clipped_src) = src_rect.intersection(&physical_clip) {
|
||||
let geometry = clipped_src.translate(offset).round();
|
||||
let origin = (geometry.origin - offset.round()).cast::<usize>();
|
||||
let actual_x = origin.x - src_rect.origin.x as usize;
|
||||
let actual_y = origin.y - src_rect.origin.y as usize;
|
||||
let stride = glyph.width.get() as u16;
|
||||
let geometry = geometry.cast();
|
||||
|
||||
match &glyph.alpha_map {
|
||||
fonts::GlyphAlphaMap::Static(data) => {
|
||||
self.processor.process_texture(
|
||||
geometry,
|
||||
SceneTexture {
|
||||
data: &data[actual_x + actual_y * stride as usize..],
|
||||
stride,
|
||||
source_size: geometry.size,
|
||||
format: PixelFormat::AlphaMap,
|
||||
color,
|
||||
// color already is mixed with global alpha
|
||||
alpha: color.alpha(),
|
||||
},
|
||||
);
|
||||
}
|
||||
fonts::GlyphAlphaMap::Shared(data) => {
|
||||
self.processor.process_shared_image_buffer(
|
||||
geometry,
|
||||
SharedBufferCommand {
|
||||
buffer: SharedBufferData::AlphaMap {
|
||||
data: data.clone(),
|
||||
width: stride,
|
||||
match &glyph.alpha_map {
|
||||
fonts::GlyphAlphaMap::Static(data) => {
|
||||
self.processor.process_texture(
|
||||
geometry,
|
||||
SceneTexture {
|
||||
data: &data[actual_x + actual_y * stride as usize..],
|
||||
stride,
|
||||
source_size: geometry.size,
|
||||
format: PixelFormat::AlphaMap,
|
||||
color,
|
||||
// color already is mixed with global alpha
|
||||
alpha: color.alpha(),
|
||||
},
|
||||
source_rect: PhysicalRect::new(
|
||||
PhysicalPoint::new(actual_x as _, actual_y as _),
|
||||
geometry.size,
|
||||
),
|
||||
colorize: color,
|
||||
// color already is mixed with global alpha
|
||||
alpha: color.alpha(),
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
);
|
||||
}
|
||||
fonts::GlyphAlphaMap::Shared(data) => {
|
||||
self.processor.process_shared_image_buffer(
|
||||
geometry,
|
||||
SharedBufferCommand {
|
||||
buffer: SharedBufferData::AlphaMap {
|
||||
data: data.clone(),
|
||||
width: stride,
|
||||
},
|
||||
source_rect: PhysicalRect::new(
|
||||
PhysicalPoint::new(actual_x as _, actual_y as _),
|
||||
geometry.size,
|
||||
),
|
||||
colorize: color,
|
||||
// color already is mixed with global alpha
|
||||
alpha: color.alpha(),
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
core::ops::ControlFlow::Continue(())
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
/// Returns the color, mixed with the current_state's alpha
|
||||
|
|
|
@ -99,15 +99,15 @@ pub struct TextParagraphLayout<'a, Font: AbstractFont> {
|
|||
impl<'a, Font: AbstractFont> TextParagraphLayout<'a, Font> {
|
||||
/// Layout the given string in lines, and call the `layout_line` callback with the line to draw at position y.
|
||||
/// The signature of the `layout_line` function is: `(glyph_iterator, line_x, line_y)`.
|
||||
/// Returns the baseline y coordinate.
|
||||
pub fn layout_lines(
|
||||
/// Returns the baseline y coordinate as Ok, or the break value if `line_callback` returns `core::ops::ControlFlow::Break`.
|
||||
pub fn layout_lines<R>(
|
||||
&self,
|
||||
mut line_callback: impl FnMut(
|
||||
&mut dyn Iterator<Item = PositionedGlyph<Font::Length>>,
|
||||
Font::Length,
|
||||
Font::Length,
|
||||
),
|
||||
) -> Font::Length {
|
||||
) -> core::ops::ControlFlow<R>,
|
||||
) -> Result<Font::Length, R> {
|
||||
let wrap = self.wrap == TextWrap::WordWrap;
|
||||
let elide_glyph = if self.overflow == TextOverflow::Elide {
|
||||
self.layout.font.glyph_for_char('…').filter(|glyph| glyph.glyph_id.is_some())
|
||||
|
@ -186,21 +186,35 @@ impl<'a, Font: AbstractFont> TextParagraphLayout<'a, Font> {
|
|||
})
|
||||
});
|
||||
|
||||
line_callback(&mut positioned_glyph_it, x, y);
|
||||
if let core::ops::ControlFlow::Break(break_val) =
|
||||
line_callback(&mut positioned_glyph_it, x, y)
|
||||
{
|
||||
return core::ops::ControlFlow::Break(break_val);
|
||||
}
|
||||
y += self.layout.font.height();
|
||||
|
||||
core::ops::ControlFlow::Continue(())
|
||||
};
|
||||
|
||||
if let Some(lines_vec) = text_lines.take() {
|
||||
for line in lines_vec {
|
||||
process_line(&line, &shape_buffer.glyphs);
|
||||
if let core::ops::ControlFlow::Break(break_val) =
|
||||
process_line(&line, &shape_buffer.glyphs)
|
||||
{
|
||||
return Err(break_val);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for line in new_line_break_iter() {
|
||||
process_line(&line, &shape_buffer.glyphs);
|
||||
if let core::ops::ControlFlow::Break(break_val) =
|
||||
process_line(&line, &shape_buffer.glyphs)
|
||||
{
|
||||
return Err(break_val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
baseline_y
|
||||
Ok(baseline_y)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,11 +297,16 @@ fn test_elision() {
|
|||
overflow: TextOverflow::Elide,
|
||||
single_line: true,
|
||||
};
|
||||
paragraph.layout_lines(|glyphs, _, _| {
|
||||
lines.push(
|
||||
glyphs.map(|positioned_glyph| positioned_glyph.glyph_id.clone()).collect::<Vec<_>>(),
|
||||
);
|
||||
});
|
||||
paragraph
|
||||
.layout_lines::<()>(|glyphs, _, _| {
|
||||
lines.push(
|
||||
glyphs
|
||||
.map(|positioned_glyph| positioned_glyph.glyph_id.clone())
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
core::ops::ControlFlow::Continue(())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(lines.len(), 1);
|
||||
let rendered_text = lines[0]
|
||||
|
@ -319,11 +338,16 @@ fn test_exact_fit() {
|
|||
overflow: TextOverflow::Elide,
|
||||
single_line: true,
|
||||
};
|
||||
paragraph.layout_lines(|glyphs, _, _| {
|
||||
lines.push(
|
||||
glyphs.map(|positioned_glyph| positioned_glyph.glyph_id.clone()).collect::<Vec<_>>(),
|
||||
);
|
||||
});
|
||||
paragraph
|
||||
.layout_lines::<()>(|glyphs, _, _| {
|
||||
lines.push(
|
||||
glyphs
|
||||
.map(|positioned_glyph| positioned_glyph.glyph_id.clone())
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
core::ops::ControlFlow::Continue(())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(lines.len(), 1);
|
||||
let rendered_text = lines[0]
|
||||
|
@ -355,11 +379,16 @@ fn test_no_line_separators_characters_rendered() {
|
|||
overflow: TextOverflow::Clip,
|
||||
single_line: true,
|
||||
};
|
||||
paragraph.layout_lines(|glyphs, _, _| {
|
||||
lines.push(
|
||||
glyphs.map(|positioned_glyph| positioned_glyph.glyph_id.clone()).collect::<Vec<_>>(),
|
||||
);
|
||||
});
|
||||
paragraph
|
||||
.layout_lines::<()>(|glyphs, _, _| {
|
||||
lines.push(
|
||||
glyphs
|
||||
.map(|positioned_glyph| positioned_glyph.glyph_id.clone())
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
core::ops::ControlFlow::Continue(())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(lines.len(), 2);
|
||||
let rendered_text = lines
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue