Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Simplify text measuring and rendering #486

Merged
merged 5 commits into from
Feb 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions crates/components/src/gesture_area.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,13 @@ mod test {
rsx!(
GestureArea {
ongesture,
"{value}"
rect {
width: "100%",
height: "100%",

}
}
"{value}"
)
}

Expand All @@ -207,7 +212,7 @@ mod test {
// Initial state
utils.wait_for_update().await;

assert_eq!(utils.root().get(0).get(0).text(), Some("EMPTY"));
assert_eq!(utils.root().get(1).text(), Some("EMPTY"));

utils.push_event(FreyaEvent::Touch {
name: "touchstart".to_string(),
Expand Down Expand Up @@ -241,7 +246,7 @@ mod test {
utils.wait_for_update().await;
utils.wait_for_update().await;

assert_eq!(utils.root().get(0).get(0).text(), Some("DoubleTap"));
assert_eq!(utils.root().get(1).text(), Some("DoubleTap"));
}

/// Simulates `TapUp` and `TapDown` gestures.
Expand All @@ -257,8 +262,13 @@ mod test {
rsx!(
GestureArea {
ongesture,
"{value}"
rect {
width: "100%",
height: "100%",

}
}
"{value}"
)
}

Expand All @@ -267,7 +277,7 @@ mod test {
// Initial state
utils.wait_for_update().await;

assert_eq!(utils.root().get(0).get(0).text(), Some("EMPTY"));
assert_eq!(utils.root().get(1).text(), Some("EMPTY"));

utils.push_event(FreyaEvent::Touch {
name: "touchstart".to_string(),
Expand All @@ -280,7 +290,7 @@ mod test {
utils.wait_for_update().await;
utils.wait_for_update().await;

assert_eq!(utils.root().get(0).get(0).text(), Some("TapDown"));
assert_eq!(utils.root().get(1).text(), Some("TapDown"));

utils.push_event(FreyaEvent::Touch {
name: "touchend".to_string(),
Expand All @@ -293,6 +303,6 @@ mod test {
utils.wait_for_update().await;
utils.wait_for_update().await;

assert_eq!(utils.root().get(0).get(0).text(), Some("TapUp"));
assert_eq!(utils.root().get(1).text(), Some("TapUp"));
}
}
81 changes: 28 additions & 53 deletions crates/core/src/layout/skia_measurer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,70 +35,30 @@ impl<'a> LayoutMeasurer<NodeId> for SkiaMeasurer<'a> {
&mut self,
node_id: NodeId,
_node: &Node,
area: &Area,
_parent_area: &Area,
available_parent_area: &Area,
) -> Option<Area> {
) -> Option<Size2D> {
let node = self.rdom.get(node_id).unwrap();
let node_type = node.node_type();

match &*node_type {
NodeType::Text(TextNode { text, .. }) => {
let text_paragraph =
create_text(&node, available_parent_area, self.font_collection, text);

Some(Area::new(
area.origin,
Size2D::new(text_paragraph.longest_line(), text_paragraph.height()),
))
NodeType::Element(ElementNode { tag, .. }) if tag == "label" => {
let label = create_label(&node, available_parent_area, self.font_collection);

Some(Size2D::new(label.longest_line(), label.height()))
}
NodeType::Element(ElementNode { tag, .. }) if tag == "paragraph" => {
let paragraph =
create_paragraph(&node, available_parent_area, self.font_collection, false);

Some(Area::new(
available_parent_area.origin,
Size2D::new(paragraph.longest_line(), paragraph.height()),
))
Some(Size2D::new(paragraph.longest_line(), paragraph.height()))
}
_ => None,
}
}
}

/// Collect all the texts and FontStyles from all the given Node's children
pub fn get_inner_texts(node: &DioxusNode) -> Vec<(FontStyleState, String)> {
node.children()
.iter()
.filter_map(|child| {
if let NodeType::Element(ElementNode { tag, .. }) = &*child.node_type() {
if tag != "text" {
return None;
}

let children = child.children();
let child_text = *children.first().unwrap();
let child_text_type = &*child_text.node_type();

if let NodeType::Text(TextNode { text, .. }) = child_text_type {
let font_style = child.get::<FontStyleState>().unwrap();
Some((font_style.clone(), text.to_owned()))
} else {
None
}
} else {
None
}
})
.collect()
}

pub fn create_text(
node: &DioxusNode,
area: &Area,
font_collection: &FontCollection,
text: &str,
) -> Paragraph {
pub fn create_label(node: &DioxusNode, area: &Area, font_collection: &FontCollection) -> Paragraph {
let font_style = &*node.get::<FontStyleState>().unwrap();

let mut paragraph_style = ParagraphStyle::default();
Expand All @@ -112,7 +72,12 @@ pub fn create_text(
}

let mut paragraph_builder = ParagraphBuilder::new(&paragraph_style, font_collection);
paragraph_builder.add_text(text);

for child in node.children() {
if let NodeType::Text(TextNode { text, .. }) = &*child.node_type() {
paragraph_builder.add_text(text);
}
}

let mut paragraph = paragraph_builder.build();
paragraph.layout(area.width() + 1.0);
Expand Down Expand Up @@ -142,11 +107,21 @@ pub fn create_paragraph(

paragraph_builder.push_style(&font_style.into());

let texts = get_inner_texts(node);

for (font_style, text) in texts.into_iter() {
paragraph_builder.push_style(&TextStyle::from(&font_style));
paragraph_builder.add_text(text);
for text_span in node.children() {
match &*text_span.node_type() {
NodeType::Element(ElementNode { tag, .. }) if tag == "text" => {
let text_nodes = text_span.children();
let text_node = *text_nodes.first().unwrap();
let text_node_type = &*text_node.node_type();

if let NodeType::Text(TextNode { text, .. }) = text_node_type {
let font_style = text_node.get::<FontStyleState>().unwrap();
paragraph_builder.push_style(&TextStyle::from(&*font_style));
paragraph_builder.add_text(text);
}
}
_ => {}
}
}

if node_cursor_settings.position.is_some() && is_rendering {
Expand Down
29 changes: 5 additions & 24 deletions crates/renderer/src/elements/label.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
use dioxus_native_core::node::NodeType;
use dioxus_native_core::prelude::TextNode;
use dioxus_native_core::real_dom::NodeImmutable;
use freya_core::layout::create_text;
use freya_core::layout::create_label;
use freya_dom::prelude::DioxusNode;
use freya_engine::prelude::*;
use torin::geometry::Area;
Expand All @@ -13,26 +10,10 @@ pub fn render_label(
canvas: &Canvas,
font_collection: &mut FontCollection,
) {
let node_children = node_ref.children();
let paragraph = create_label(node_ref, area, font_collection);

let child = node_children.first();
let x = area.min_x();
let y = area.min_y();

let text = if let Some(child) = child {
if let NodeType::Text(TextNode { text, .. }) = &*child.node_type() {
Some(text.clone())
} else {
None
}
} else {
None
};

if let Some(text) = text {
let paragraph = create_text(node_ref, area, font_collection, &text);

let x = area.min_x();
let y = area.min_y();

paragraph.paint(canvas, (x, y));
}
paragraph.paint(canvas, (x, y));
}
3 changes: 1 addition & 2 deletions crates/torin/benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@ impl LayoutMeasurer<usize> for TestingMeasurer {
&mut self,
_node_id: usize,
_node: &Node,
_area: &Area,
_parent_size: &Area,
_available_parent_area: &Area,
) -> Option<Area> {
) -> Option<Size2D> {
None
}
}
Expand Down
3 changes: 1 addition & 2 deletions crates/torin/examples/demo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@ impl LayoutMeasurer<usize> for CustomMeasurer {
&mut self,
_node_id: usize,
_node: &Node,
_area: &Area,
_parent_size: &Area,
_available_parent_area: &Area,
) -> Option<Area> {
) -> Option<Size2D> {
None
}
}
Expand Down
9 changes: 6 additions & 3 deletions crates/torin/src/custom_measurer.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
use crate::{dom_adapter::NodeKey, geometry::Area, node::Node};
use crate::{
dom_adapter::NodeKey,
geometry::{Area, Size2D},
node::Node,
};

pub trait LayoutMeasurer<Key: NodeKey> {
fn measure(
&mut self,
node_id: Key,
node: &Node,
area: &Area,
parent_area: &Area,
available_parent_area: &Area,
) -> Option<Area>;
) -> Option<Size2D>;
}
52 changes: 25 additions & 27 deletions crates/torin/src/measure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,25 +60,17 @@ pub fn measure_node<Key: NodeKey>(
layout_metadata.root_area.height(),
);

// 3. Compute the origin of the area
let area_origin = node
.position
.get_origin(available_parent_area, parent_area, &area_size);

let mut area = Rect::new(area_origin, area_size);

// 4. If available, run a custom layout measure function
// 3. If available, run a custom layout measure function
// This is useful when you use third-party libraries (e.g. rust-skia, cosmic-text) to measure text layouts
// When a Node is measured by a custom measurer function the inner children will be skipped
let measure_inner_children = if let Some(measurer) = measurer {
let custom_area =
measurer.measure(node_id, node, &area, parent_area, available_parent_area);
let custom_size = measurer.measure(node_id, node, parent_area, available_parent_area);

// 4.1. Compute the width and height again using the new custom area sizes
if let Some(custom_area) = custom_area {
// 3.1. Compute the width and height again using the new custom area sizes
if let Some(custom_size) = custom_size {
if Size::Inner == node.width {
area.size.width = node.width.min_max(
custom_area.width(),
area_size.width = node.width.min_max(
custom_size.width,
parent_area.size.width,
available_parent_area.size.width,
node.margin.left(),
Expand All @@ -89,8 +81,8 @@ pub fn measure_node<Key: NodeKey>(
);
}
if Size::Inner == node.height {
area.size.height = node.height.min_max(
custom_area.height(),
area_size.height = node.height.min_max(
custom_size.height,
parent_area.size.height,
available_parent_area.size.height,
node.margin.top(),
Expand All @@ -103,18 +95,18 @@ pub fn measure_node<Key: NodeKey>(
}

// Do not measure inner children
custom_area.is_none()
custom_size.is_none()
} else {
true
};

// 5. Compute the inner area of the Node, which is basically the area inside the margins and paddings
let mut inner_area = {
let mut inner_area = area;
// 4. Compute the inner size of the Node, which is basically the size inside the margins and paddings
let inner_size = {
let mut inner_size = area_size;

// 5.1. When having an unsized bound we set it to whatever is still available in the parent's area
// 4.1. When having an unsized bound we set it to whatever is still available in the parent's area
if Size::Inner == node.width {
inner_area.size.width = node.width.min_max(
inner_size.width = node.width.min_max(
available_parent_area.width(),
parent_area.size.width,
available_parent_area.width(),
Expand All @@ -126,7 +118,7 @@ pub fn measure_node<Key: NodeKey>(
);
}
if Size::Inner == node.height {
inner_area.size.height = node.height.min_max(
inner_size.height = node.height.min_max(
available_parent_area.height(),
parent_area.size.height,
available_parent_area.height(),
Expand All @@ -137,12 +129,18 @@ pub fn measure_node<Key: NodeKey>(
layout_metadata.root_area.height(),
);
}

inner_area
.after_gaps(&node.padding)
.after_gaps(&node.margin)
inner_size
};

// 5. Create the areas
let area_origin = node
.position
.get_origin(available_parent_area, parent_area, &area_size);
let mut area = Rect::new(area_origin, area_size);
let mut inner_area = Rect::new(area_origin, inner_size)
.after_gaps(&node.padding)
.after_gaps(&node.margin);

let mut inner_sizes = Size2D::default();

if measure_inner_children {
Expand Down
3 changes: 1 addition & 2 deletions crates/torin/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ impl LayoutMeasurer<usize> for TestingMeasurer {
&mut self,
_node_id: usize,
_node: &Node,
_area: &Area,
_parent_size: &Area,
_available_parent_area: &Area,
) -> Option<Area> {
) -> Option<Size2D> {
None
}
}
Expand Down
Loading