Skip to content

Commit 94cda8e

Browse files
committed
Handle a lot more layout options
1 parent 2e846be commit 94cda8e

File tree

3 files changed

+137
-60
lines changed

3 files changed

+137
-60
lines changed

internal/renderers/femtovg/fonts.rs

Lines changed: 82 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ use crate::{PhysicalLength, PhysicalPoint};
77
use core::num::NonZeroUsize;
88
use femtovg::TextContext;
99
use i_slint_common::sharedfontique::{self, parley};
10-
use i_slint_core::{items::TextHorizontalAlignment, lengths::LogicalLength};
10+
use i_slint_core::{
11+
graphics::FontRequest,
12+
items::{TextHorizontalAlignment, TextVerticalAlignment, TextWrap},
13+
lengths::LogicalLength,
14+
};
1115
use std::cell::RefCell;
1216
use std::collections::HashMap;
1317

@@ -42,34 +46,85 @@ thread_local! {
4246
pub static FONT_CACHE: RefCell<FontCache> = RefCell::new(Default::default())
4347
}
4448

45-
pub fn layout(
46-
text: &str,
47-
max_width: Option<PhysicalLength>,
48-
horizontal_align: TextHorizontalAlignment,
49-
stroke: Option<sharedfontique::BrushTextStrokeStyle>,
50-
selection: Option<std::ops::Range<usize>>,
51-
) -> parley::Layout<sharedfontique::Brush> {
49+
pub struct LayoutOptions {
50+
pub max_width: Option<PhysicalLength>,
51+
pub horizontal_align: TextHorizontalAlignment,
52+
pub stroke: Option<sharedfontique::BrushTextStrokeStyle>,
53+
pub selection: Option<std::ops::Range<usize>>,
54+
pub font_request: Option<FontRequest>,
55+
pub text_wrap: TextWrap,
56+
}
57+
58+
impl Default for LayoutOptions {
59+
fn default() -> Self {
60+
Self {
61+
max_width: None,
62+
horizontal_align: TextHorizontalAlignment::Left,
63+
stroke: None,
64+
selection: None,
65+
font_request: None,
66+
text_wrap: TextWrap::WordWrap,
67+
}
68+
}
69+
}
70+
71+
pub fn layout(text: &str, options: LayoutOptions) -> parley::Layout<sharedfontique::Brush> {
5272
let mut font_context = sharedfontique::font_context();
5373
let mut layout_context = sharedfontique::layout_context();
5474

5575
let mut builder = layout_context.ranged_builder(&mut font_context, text, 1.0, true);
56-
builder.push_default(parley::StyleProperty::FontSize(16.0));
76+
if let Some(ref font_request) = options.font_request {
77+
if let Some(family) = &font_request.family {
78+
builder.push_default(parley::StyleProperty::FontStack(
79+
parley::style::FontStack::Single(parley::style::FontFamily::Named(
80+
family.as_str().into(),
81+
)),
82+
));
83+
}
84+
if let Some(weight) = font_request.weight {
85+
builder.push_default(parley::StyleProperty::FontWeight(
86+
parley::style::FontWeight::new(weight as f32),
87+
));
88+
}
89+
if let Some(letter_spacing) = font_request.letter_spacing {
90+
builder.push_default(parley::StyleProperty::LetterSpacing(letter_spacing.get()));
91+
}
92+
builder.push_default(parley::StyleProperty::FontStyle(if font_request.italic {
93+
parley::style::FontStyle::Italic
94+
} else {
95+
parley::style::FontStyle::Normal
96+
}));
97+
}
98+
let pixel_size = options
99+
.font_request
100+
.and_then(|font_request| font_request.pixel_size)
101+
.unwrap_or(DEFAULT_FONT_SIZE);
102+
builder.push_default(parley::StyleProperty::FontSize(pixel_size.get()));
103+
builder.push_default(parley::StyleProperty::WordBreak(match options.text_wrap {
104+
TextWrap::NoWrap => parley::style::WordBreakStrength::KeepAll,
105+
TextWrap::WordWrap => parley::style::WordBreakStrength::Normal,
106+
TextWrap::CharWrap => parley::style::WordBreakStrength::BreakAll,
107+
}));
108+
57109
builder.push_default(parley::StyleProperty::Brush(sharedfontique::Brush {
58-
stroke,
110+
stroke: options.stroke,
59111
..Default::default()
60112
}));
61-
if let Some(selection) = selection {
113+
if let Some(selection) = options.selection {
62114
builder.push(
63-
parley::StyleProperty::Brush(sharedfontique::Brush { stroke, is_selected: true }),
115+
parley::StyleProperty::Brush(sharedfontique::Brush {
116+
stroke: options.stroke,
117+
is_selected: true,
118+
}),
64119
selection,
65120
);
66121
}
67122

68123
let mut layout: parley::Layout<sharedfontique::Brush> = builder.build(text);
69-
layout.break_all_lines(max_width.map(|max_width| max_width.get()));
124+
layout.break_all_lines(options.max_width.map(|max_width| max_width.get()));
70125
layout.align(
71-
max_width.map(|max_width| max_width.get()),
72-
match horizontal_align {
126+
options.max_width.map(|max_width| max_width.get()),
127+
match options.horizontal_align {
73128
TextHorizontalAlignment::Left => parley::Alignment::Left,
74129
TextHorizontalAlignment::Center => parley::Alignment::Middle,
75130
TextHorizontalAlignment::Right => parley::Alignment::Right,
@@ -110,3 +165,15 @@ pub fn get_cursor_location_and_size(
110165

111166
cursor_point
112167
}
168+
169+
pub fn get_offset(
170+
vertical_align: TextVerticalAlignment,
171+
max_height: PhysicalLength,
172+
layout: &parley::Layout<sharedfontique::Brush>,
173+
) -> f32 {
174+
match vertical_align {
175+
TextVerticalAlignment::Top => 0.0,
176+
TextVerticalAlignment::Center => (max_height.get() - layout.height()) / 2.0,
177+
TextVerticalAlignment::Bottom => max_height.get() - layout.height(),
178+
}
179+
}

internal/renderers/femtovg/itemrenderer.rs

Lines changed: 27 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use i_slint_core::item_rendering::{
1818
};
1919
use i_slint_core::items::{
2020
self, Clip, FillRule, ImageRendering, ImageTiling, ItemRc, Layer, Opacity, RenderingResult,
21-
TextHorizontalAlignment, TextStrokeStyle, TextVerticalAlignment,
21+
TextStrokeStyle,
2222
};
2323
use i_slint_core::lengths::{
2424
LogicalBorderRadius, LogicalLength, LogicalPoint, LogicalRect, LogicalSize, LogicalVector,
@@ -264,18 +264,6 @@ fn draw_glyphs<R: femtovg::Renderer + TextureImporter>(
264264
}
265265
}
266266

267-
fn get_offset(
268-
vertical_align: TextVerticalAlignment,
269-
max_height: PhysicalLength,
270-
layout: &parley::Layout<sharedfontique::Brush>,
271-
) -> f32 {
272-
match vertical_align {
273-
TextVerticalAlignment::Top => 0.0,
274-
TextVerticalAlignment::Center => (max_height.get() - layout.height()) / 2.0,
275-
TextVerticalAlignment::Bottom => max_height.get() - layout.height(),
276-
}
277-
}
278-
279267
impl<'a, R: femtovg::Renderer + TextureImporter> ItemRenderer for GLItemRenderer<'a, R> {
280268
fn draw_rectangle(
281269
&mut self,
@@ -407,7 +395,7 @@ impl<'a, R: femtovg::Renderer + TextureImporter> ItemRenderer for GLItemRenderer
407395
fn draw_text(
408396
&mut self,
409397
text: Pin<&dyn RenderText>,
410-
_self_rc: &ItemRc,
398+
self_rc: &ItemRc,
411399
size: LogicalSize,
412400
_cache: &CachedRenderingData,
413401
) {
@@ -425,12 +413,12 @@ impl<'a, R: femtovg::Renderer + TextureImporter> ItemRenderer for GLItemRenderer
425413
let (horizontal_align, vertical_align) = text.alignment();
426414
let color = text.color();
427415
// TODO
428-
let _wrap = text.wrap();
429-
// TODO
430416
let _overflow = text.overflow();
431417
// TODO
432418
let _letter_spacing = text.letter_spacing();
433419

420+
let font_request = text.font_request(self_rc);
421+
434422
let text_path = rect_to_path((size * self.scale_factor).into());
435423
let paint = match self.brush_to_paint(color, &text_path) {
436424
Some(paint) => paint,
@@ -462,25 +450,29 @@ impl<'a, R: femtovg::Renderer + TextureImporter> ItemRenderer for GLItemRenderer
462450

463451
let layout = fonts::layout(
464452
text.text().as_str(),
465-
Some(max_width),
466-
horizontal_align,
467-
stroke_paint.is_some().then_some(stroke_style).map(|style| match style {
468-
TextStrokeStyle::Outside => sharedfontique::BrushTextStrokeStyle::Outside,
469-
TextStrokeStyle::Center => sharedfontique::BrushTextStrokeStyle::Center,
470-
}),
471-
None,
453+
fonts::LayoutOptions {
454+
horizontal_align,
455+
max_width: Some(max_width),
456+
stroke: stroke_paint.is_some().then_some(stroke_style).map(|style| match style {
457+
TextStrokeStyle::Outside => sharedfontique::BrushTextStrokeStyle::Outside,
458+
TextStrokeStyle::Center => sharedfontique::BrushTextStrokeStyle::Center,
459+
}),
460+
font_request: Some(font_request),
461+
text_wrap: text.wrap(),
462+
..Default::default()
463+
},
472464
);
473465

474466
let mut canvas = self.canvas.borrow_mut();
475-
let offset = get_offset(vertical_align, max_height, &layout);
467+
let offset = fonts::get_offset(vertical_align, max_height, &layout);
476468

477469
draw_glyphs(&layout, &mut canvas, &paint, offset, Color::default(), Color::default());
478470
}
479471

480472
fn draw_text_input(
481473
&mut self,
482474
text_input: Pin<&items::TextInput>,
483-
_self_rc: &ItemRc,
475+
self_rc: &ItemRc,
484476
size: LogicalSize,
485477
) {
486478
let width = size.width_length() * self.scale_factor;
@@ -493,6 +485,8 @@ impl<'a, R: femtovg::Renderer + TextureImporter> ItemRenderer for GLItemRenderer
493485
return;
494486
}
495487

488+
let font_request = text_input.font_request(self_rc);
489+
496490
let visual_representation = text_input.visual_representation(None);
497491

498492
let paint = match self.brush_to_paint(
@@ -521,12 +515,14 @@ impl<'a, R: femtovg::Renderer + TextureImporter> ItemRenderer for GLItemRenderer
521515

522516
let layout = fonts::layout(
523517
&text,
524-
Some(width),
525-
TextHorizontalAlignment::Left,
526-
None,
527-
Some(min_select..max_select),
518+
fonts::LayoutOptions {
519+
max_width: Some(width),
520+
selection: Some(min_select..max_select),
521+
font_request: Some(font_request),
522+
..Default::default()
523+
},
528524
);
529-
let offset = get_offset(text_input.vertical_alignment(), height, &layout);
525+
let offset = fonts::get_offset(text_input.vertical_alignment(), height, &layout);
530526
draw_glyphs(
531527
&layout,
532528
&mut canvas,
@@ -979,7 +975,7 @@ impl<'a, R: femtovg::Renderer + TextureImporter> ItemRenderer for GLItemRenderer
979975
}
980976

981977
fn draw_string(&mut self, string: &str, color: Color) {
982-
let layout = fonts::layout(string, None, TextHorizontalAlignment::Left, None, None);
978+
let layout = fonts::layout(string, Default::default());
983979
let paint = femtovg::Paint::color(to_femtovg_color(&color));
984980
let mut canvas = self.canvas.borrow_mut();
985981
draw_glyphs(&layout, &mut canvas, &paint, 0.0, color, color);

internal/renderers/femtovg/lib.rs

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use i_slint_core::graphics::{euclid, rendering_metrics_collector::RenderingMetri
1515
use i_slint_core::graphics::{BorderRadius, Rgba8Pixel};
1616
use i_slint_core::graphics::{FontRequest, SharedPixelBuffer};
1717
use i_slint_core::item_rendering::ItemRenderer;
18-
use i_slint_core::items::{TextHorizontalAlignment, TextWrap};
18+
use i_slint_core::items::TextWrap;
1919
use i_slint_core::lengths::{
2020
LogicalLength, LogicalPoint, LogicalRect, LogicalSize, PhysicalPx, ScaleFactor,
2121
};
@@ -281,19 +281,20 @@ impl<B: GraphicsBackend> FemtoVGRenderer<B> {
281281
impl<B: GraphicsBackend> RendererSealed for FemtoVGRenderer<B> {
282282
fn text_size(
283283
&self,
284-
// TODO
285-
_font_request: i_slint_core::graphics::FontRequest,
284+
font_request: i_slint_core::graphics::FontRequest,
286285
text: &str,
287286
max_width: Option<LogicalLength>,
288287
scale_factor: ScaleFactor,
289-
_text_wrap: TextWrap, //TODO: Add support for char-wrap
288+
text_wrap: TextWrap,
290289
) -> LogicalSize {
291290
let layout = fonts::layout(
292291
text,
293-
max_width.map(|max_width| max_width * scale_factor),
294-
TextHorizontalAlignment::Left,
295-
None,
296-
None,
292+
fonts::LayoutOptions {
293+
max_width: max_width.map(|max_width| max_width * scale_factor),
294+
text_wrap,
295+
font_request: Some(font_request),
296+
..Default::default()
297+
},
297298
);
298299
LogicalSize::new(layout.width(), layout.height())
299300
}
@@ -310,22 +311,32 @@ impl<B: GraphicsBackend> RendererSealed for FemtoVGRenderer<B> {
310311
&self,
311312
text_input: Pin<&i_slint_core::items::TextInput>,
312313
pos: LogicalPoint,
313-
// TODO
314-
_font_request: FontRequest,
314+
font_request: FontRequest,
315315
scale_factor: ScaleFactor,
316316
) -> usize {
317317
let pos = pos * scale_factor;
318318
let text = text_input.text();
319319

320-
// TODO
321-
let result = text.len();
322-
323320
let width = text_input.width() * scale_factor;
324321
let height = text_input.height() * scale_factor;
325322
if width.get() <= 0. || height.get() <= 0. || pos.y < 0. {
326323
return 0;
327324
}
328325

326+
let layout = fonts::layout(
327+
&text,
328+
fonts::LayoutOptions {
329+
font_request: Some(font_request),
330+
max_width: Some(width),
331+
332+
..Default::default()
333+
},
334+
);
335+
let _offset = fonts::get_offset(text_input.vertical_alignment(), height, &layout);
336+
337+
// TODO
338+
let result = text.len();
339+
329340
let visual_representation = text_input.visual_representation(None);
330341

331342
visual_representation.map_byte_offset_from_byte_offset_in_visual_text(result)
@@ -351,7 +362,10 @@ impl<B: GraphicsBackend> RendererSealed for FemtoVGRenderer<B> {
351362
);
352363
}
353364

354-
let layout = fonts::layout(&text, Some(width), TextHorizontalAlignment::Left, None, None);
365+
let layout = fonts::layout(
366+
&text,
367+
fonts::LayoutOptions { max_width: Some(width), ..Default::default() },
368+
);
355369
let cursor_position = fonts::get_cursor_location_and_size(&layout, byte_offset, 0.0)
356370
.map(|location| location.0);
357371

0 commit comments

Comments
 (0)