diff --git a/src/algo.rs b/src/algo.rs
index 6014c17cb..03e538436 100644
--- a/src/algo.rs
+++ b/src/algo.rs
@@ -11,10 +11,10 @@ use crate::number::*;
use crate::geometry::{Point, Rect, Size};
-#[derive(Debug)]
-struct ComputeResult {
- size: Size<f32>,
- children: Vec<layout::Node>,
+#[derive(Debug, Clone)]
+pub struct ComputeResult {
+ pub size: Size<f32>,
+ pub children: Vec<layout::Node>,
}
struct FlexItem<'a> {
@@ -115,35 +115,54 @@ fn compute_internal(
percent_calc_base: Number,
perform_layout: bool,
) -> ComputeResult {
- // If this is a leaf node we can skip a lot of this function in some cases
- if node.children.is_empty() {
- if node_size.width.is_defined() && node_size.height.is_defined() {
- return ComputeResult { size: node_size.map(|s| s.or_else(0.0)), children: vec![] };
- }
-
- // Measure function can be extremely expensive to call so cache calls to it
- if let Some(ref measure) = node.measure {
- if let Some(cache) = node.measure_cache.get() {
+ // First we check if we have a result for the given input
+ {
+ let layout_cache = node.layout_cache.borrow();
+ if layout_cache.is_some() {
+ let cache = layout_cache.as_ref().unwrap();
+ if cache.perform_layout || !perform_layout {
let width_compatible = if let Number::Defined(width) = node_size.width {
- (width - cache.result.width).abs() < f32::EPSILON
+ (width - cache.result.size.width).abs() < f32::EPSILON
} else {
- cache.constraint.width.is_undefined()
+ cache.node_size.width.is_undefined()
};
let height_compatible = if let Number::Defined(height) = node_size.height {
- (height - cache.result.height).abs() < f32::EPSILON
+ (height - cache.result.size.height).abs() < f32::EPSILON
} else {
- cache.constraint.height.is_undefined()
+ cache.node_size.height.is_undefined()
};
- if cache.constraint == node_size || (width_compatible && height_compatible) {
- return ComputeResult { size: cache.result, children: vec![] };
+ if width_compatible && height_compatible {
+ return cache.result.clone();
+ }
+
+ if cache.node_size == node_size
+ && cache.parent_size == parent_size
+ && cache.percent_calc_base == percent_calc_base
+ {
+ return cache.result.clone();
}
}
+ }
+ }
+
+ // If this is a leaf node we can skip a lot of this function in some cases
+ if node.children.is_empty() {
+ if node_size.width.is_defined() && node_size.height.is_defined() {
+ return ComputeResult { size: node_size.map(|s| s.or_else(0.0)), children: vec![] };
+ }
- let size = measure(node_size);
- node.measure_cache.set(Some(MeasureCache { constraint: node_size, result: size }));
- return ComputeResult { size, children: vec![] };
+ if let Some(ref measure) = node.measure {
+ let result = ComputeResult { size: measure(node_size), children: vec![] };
+ node.layout_cache.replace(Some(LayoutCache {
+ node_size,
+ parent_size,
+ percent_calc_base,
+ perform_layout,
+ result: result.clone(),
+ }));
+ return result;
}
}
@@ -1005,7 +1024,15 @@ fn compute_internal(
// We have the container size. If our caller does not care about performing
// layout we are done now.
if !perform_layout {
- return ComputeResult { size: container_size, children: vec![] };
+ let result = ComputeResult { size: container_size, children: vec![] };
+ node.layout_cache.replace(Some(LayoutCache {
+ node_size,
+ parent_size,
+ percent_calc_base,
+ perform_layout,
+ result: result.clone(),
+ }));
+ return result;
}
// 16. Align all flex lines per align-content.
@@ -1277,5 +1304,14 @@ fn compute_internal(
children.append(&mut hidden_children);
children.sort_by(|c1, c2| c1.order.cmp(&c2.order));
- ComputeResult { size: container_size, children }
+
+ let result = ComputeResult { size: container_size, children };
+ node.layout_cache.replace(Some(LayoutCache {
+ node_size,
+ parent_size,
+ percent_calc_base,
+ perform_layout,
+ result: result.clone(),
+ }));
+ result
}
diff --git a/src/layout.rs b/src/layout.rs
index eba832c68..f21c85861 100644
--- a/src/layout.rs
+++ b/src/layout.rs
@@ -1,6 +1,6 @@
use crate::geometry::{Point, Size};
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct Node {
pub(crate) order: u32,
pub size: Size<f32>,
diff --git a/src/style.rs b/src/style.rs
index 75c9e6b37..2665d0ee5 100644
--- a/src/style.rs
+++ b/src/style.rs
@@ -1,3 +1,4 @@
+use crate::algo;
use crate::geometry::{Rect, Size};
use crate::number::Number;
@@ -193,10 +194,14 @@ impl Default for Size<Dimension> {
type MeasureFunc = Box<Fn(Size<Number>) -> Size<f32>>;
-#[derive(Copy, Clone)]
-pub struct MeasureCache {
- pub constraint: Size<Number>,
- pub result: Size<f32>,
+#[derive(Debug, Clone)]
+pub struct LayoutCache {
+ pub node_size: Size<Number>,
+ pub parent_size: Size<Number>,
+ pub percent_calc_base: Number,
+ pub perform_layout: bool,
+
+ pub result: algo::ComputeResult,
}
pub struct Node {
@@ -233,7 +238,7 @@ pub struct Node {
pub children: Vec<Node>,
- pub measure_cache: std::cell::Cell<Option<MeasureCache>>,
+ pub layout_cache: std::cell::RefCell<Option<LayoutCache>>,
}
impl Default for Node {
@@ -272,7 +277,7 @@ impl Default for Node {
children: vec![],
- measure_cache: std::cell::Cell::new(None),
+ layout_cache: std::cell::RefCell::new(None),
}
}
}