From 55714e951e0b084b0c555c5eb3801563baa4cec9 Mon Sep 17 00:00:00 2001 From: marc2332 Date: Thu, 29 Aug 2024 22:00:23 +0200 Subject: [PATCH] feat: Use the actual text font height to invalidate the areas of texts instead of the one given by the line height --- crates/common/src/layout.rs | 2 +- crates/core/src/elements/label.rs | 11 +++++- crates/core/src/elements/paragraph.rs | 10 ++++- crates/core/src/render/skia_measurer.rs | 51 ++++++++++++++++++------- crates/state/src/font_style.rs | 13 +++++-- examples/counter.rs | 37 ++++++++++++++---- 6 files changed, 96 insertions(+), 28 deletions(-) diff --git a/crates/common/src/layout.rs b/crates/common/src/layout.rs index a8e00db18..449d5cfa8 100644 --- a/crates/common/src/layout.rs +++ b/crates/common/src/layout.rs @@ -27,7 +27,7 @@ pub enum CursorLayoutResponse { TextSelection { from: usize, to: usize, id: usize }, } -pub struct CachedParagraph(pub Paragraph); +pub struct CachedParagraph(pub Paragraph, pub f32); /// # Safety /// Skia `Paragraph` are neither Sync or Send, but in order to store them in the Associated diff --git a/crates/core/src/elements/label.rs b/crates/core/src/elements/label.rs index f8386909b..0c66114d2 100644 --- a/crates/core/src/elements/label.rs +++ b/crates/core/src/elements/label.rs @@ -57,7 +57,16 @@ impl ElementUtils for LabelElement { node_ref: &DioxusNode, scale_factor: f32, ) -> Area { - let area = layout_node.visible_area(); + let paragraph_font_height = &layout_node + .data + .as_ref() + .unwrap() + .get::() + .unwrap() + .1; + let mut area = layout_node.visible_area(); + area.size.height = area.size.height.max(*paragraph_font_height); + let font_style = node_ref.get::().unwrap(); let mut text_shadow_area = area; diff --git a/crates/core/src/elements/paragraph.rs b/crates/core/src/elements/paragraph.rs index 550f5dee9..44ee40a9c 100644 --- a/crates/core/src/elements/paragraph.rs +++ b/crates/core/src/elements/paragraph.rs @@ -138,7 +138,7 @@ impl ElementUtils for ParagraphElement { }; if node_cursor_state.position.is_some() { - let paragraph = create_paragraph( + let (paragraph, _) = create_paragraph( node_ref, &area.size, font_collection, @@ -182,7 +182,15 @@ impl ElementUtils for ParagraphElement { node_ref: &DioxusNode, scale_factor: f32, ) -> Area { + let paragraph_font_height = &layout_node + .data + .as_ref() + .unwrap() + .get::() + .unwrap() + .1; let mut area = layout_node.visible_area(); + area.size.height = area.size.height.max(*paragraph_font_height); // Iterate over all the text spans inside this paragraph and if any of them // has a shadow at all, apply this shadow to the general paragraph. diff --git a/crates/core/src/render/skia_measurer.rs b/crates/core/src/render/skia_measurer.rs index 4c95c087f..dd98f41a8 100644 --- a/crates/core/src/render/skia_measurer.rs +++ b/crates/core/src/render/skia_measurer.rs @@ -69,20 +69,21 @@ impl<'a> LayoutMeasurer for SkiaMeasurer<'a> { match &*node_type { NodeType::Element(ElementNode { tag, .. }) if tag == &TagName::Label => { - let label = create_label( + let (label, paragraph_font_height) = create_label( &node, area_size, self.font_collection, self.default_fonts, self.scale_factor, ); + let res = Size2D::new(label.longest_line(), label.height()); let mut map = SendAnyMap::new(); - map.insert(CachedParagraph(label)); + map.insert(CachedParagraph(label, paragraph_font_height)); Some((res, Arc::new(map))) } NodeType::Element(ElementNode { tag, .. }) if tag == &TagName::Paragraph => { - let paragraph = create_paragraph( + let (paragraph, paragraph_font_height) = create_paragraph( &node, area_size, self.font_collection, @@ -92,7 +93,7 @@ impl<'a> LayoutMeasurer for SkiaMeasurer<'a> { ); let res = Size2D::new(paragraph.longest_line(), paragraph.height()); let mut map = SendAnyMap::new(); - map.insert(CachedParagraph(paragraph)); + map.insert(CachedParagraph(paragraph, paragraph_font_height)); Some((res, Arc::new(map))) } _ => None, @@ -130,20 +131,21 @@ pub fn create_label( font_collection: &FontCollection, default_font_family: &[String], scale_factor: f32, -) -> Paragraph { +) -> (Paragraph, f32) { let font_style = &*node.get::().unwrap(); let mut paragraph_style = ParagraphStyle::default(); paragraph_style.set_text_align(font_style.text_align); paragraph_style.set_max_lines(font_style.max_lines); paragraph_style.set_replace_tab_characters(true); - let text_style = font_style.text_style(default_font_family, scale_factor); - paragraph_style.set_text_style(&text_style); if let Some(ellipsis) = font_style.text_overflow.get_ellipsis() { paragraph_style.set_ellipsis(ellipsis); } + let text_style = font_style.text_style(default_font_family, scale_factor, true); + paragraph_style.set_text_style(&text_style); + let mut paragraph_builder = ParagraphBuilder::new(¶graph_style, font_collection); for child in node.children() { @@ -154,7 +156,17 @@ pub fn create_label( let mut paragraph = paragraph_builder.build(); paragraph.layout(area_size.width + 1.0); - paragraph + + // Measure the actual text height, ignoring the line height + let mut height = paragraph.height(); + for line in paragraph.get_line_metrics() { + for (_, text) in line.get_style_metrics(0..1) { + let text_height = -(text.font_metrics.ascent - text.font_metrics.descent); + height = height.max(text_height); + } + } + + (paragraph, height) } /// Align the Y axis of the highlights and cursor of a paragraph @@ -212,7 +224,7 @@ pub fn create_paragraph( is_rendering: bool, default_font_family: &[String], scale_factor: f32, -) -> Paragraph { +) -> (Paragraph, f32) { let font_style = &*node.get::().unwrap(); let mut paragraph_style = ParagraphStyle::default(); @@ -226,10 +238,13 @@ pub fn create_paragraph( let mut paragraph_builder = ParagraphBuilder::new(¶graph_style, font_collection); - let text_style = font_style.text_style(default_font_family, scale_factor); + let text_style = font_style.text_style(default_font_family, scale_factor, true); paragraph_builder.push_style(&text_style); - for text_span in node.children() { + let node_children = node.children(); + let node_children_len = node_children.len(); + + for text_span in node_children { if let NodeType::Element(ElementNode { tag: TagName::Text, .. }) = &*text_span.node_type() @@ -238,7 +253,7 @@ pub fn create_paragraph( let text_node = *text_nodes.first().unwrap(); let text_node_type = &*text_node.node_type(); let font_style = text_span.get::().unwrap(); - let text_style = font_style.text_style(default_font_family, scale_factor); + let text_style = font_style.text_style(default_font_family, scale_factor, true); paragraph_builder.push_style(&text_style); if let NodeType::Text(text) = text_node_type { @@ -254,5 +269,15 @@ pub fn create_paragraph( let mut paragraph = paragraph_builder.build(); paragraph.layout(area_size.width + 1.0); - paragraph + + // Measure the actual text height, ignoring the line height + let mut height = paragraph.height(); + for line in paragraph.get_line_metrics() { + for (_, text) in line.get_style_metrics(0..node_children_len) { + let text_height = -(text.font_metrics.ascent - text.font_metrics.descent); + height = height.max(text_height); + } + } + + (paragraph, height) } diff --git a/crates/state/src/font_style.rs b/crates/state/src/font_style.rs index 62bba4f39..3959c41c3 100644 --- a/crates/state/src/font_style.rs +++ b/crates/state/src/font_style.rs @@ -48,7 +48,12 @@ pub struct FontStyleState { } impl FontStyleState { - pub fn text_style(&self, default_font_family: &[String], scale_factor: f32) -> TextStyle { + pub fn text_style( + &self, + default_font_family: &[String], + scale_factor: f32, + height_override: bool, + ) -> TextStyle { let mut text_style = TextStyle::new(); let mut font_family = self.font_family.clone(); @@ -65,7 +70,7 @@ impl FontStyleState { .set_font_families(&font_family) .set_word_spacing(self.word_spacing) .set_letter_spacing(self.letter_spacing) - .set_height_override(true) + .set_height_override(height_override) .set_height(self.line_height); for text_shadow in self.text_shadows.iter() { @@ -143,8 +148,8 @@ impl ParseAttribute for FontStyleState { } AttributeName::LineHeight => { if let Some(value) = attr.value.as_text() { - if let Ok(line_height) = value.parse() { - self.line_height = line_height; + if let Ok(line_height) = value.parse::() { + self.line_height = line_height.max(1.0); } } } diff --git a/examples/counter.rs b/examples/counter.rs index 9e03ce1b9..126ac123c 100644 --- a/examples/counter.rs +++ b/examples/counter.rs @@ -14,14 +14,35 @@ fn app() -> Element { rsx!( rect { - height: "50%", - width: "100%", - main_align: "center", - cross_align: "center", - background: "rgb(0, 119, 182)", - color: "white", - shadow: "0 4 20 5 rgb(0, 0, 0, 80)", - Loader {} + rect { + height: "50%", + width: "100%", + main_align: "center", + cross_align: "center", + background: "rgb(0, 119, 182)", + color: "white", + shadow: "0 4 20 5 rgb(0, 0, 0, 80)", + label { + font_size: "75", + font_weight: "bold", + "{count}" + } + } + rect { + height: "50%", + width: "100%", + main_align: "center", + cross_align: "center", + direction: "horizontal", + Button { + onclick: move |_| count += 1, + label { "Increase" } + } + Button { + onclick: move |_| count -= 1, + label { "Decrease" } + } + } } ) }