diff --git a/float-pigment-forest/Cargo.toml b/float-pigment-forest/Cargo.toml index 1a67b73..634d64f 100644 --- a/float-pigment-forest/Cargo.toml +++ b/float-pigment-forest/Cargo.toml @@ -12,7 +12,7 @@ repository.workspace = true [lib] name = "float_pigment_forest" -crate-type = ["staticlib", "cdylib", "rlib"] +crate-type = ["staticlib", "rlib"] [[bin]] name = "float_pigment_layout_cpp_binding_gen_tool" diff --git a/float-pigment-forest/src/external.rs b/float-pigment-forest/src/external.rs new file mode 100644 index 0000000..90cb0a3 --- /dev/null +++ b/float-pigment-forest/src/external.rs @@ -0,0 +1,287 @@ +use crate::{convert_node_ref_to_ptr, ffi::MeasureMode}; +use float_pigment_css::length_num::LengthNum; +use float_pigment_layout::Size; + +#[cfg(not(debug_assertions))] +use crate::ffi::{ + ExternalDirtyCallback, ExternalGetBaseline, ExternalMeasure, ExternalResolveCalc, +}; + +use crate::{Len, Node}; + +#[cfg(debug_assertions)] +use mock_external::{ + ExternalDirtyCallback, ExternalGetBaseline, ExternalMeasure, ExternalResolveCalc, +}; + +fn to_f32_with_max_to_infinity(v: Len) -> f32 { + if v == Len::MAX { + f32::INFINITY + } else { + v.to_f32() + } +} + +pub(crate) fn get_baseline_impl(node: &Node, width: Len, height: Len) -> Option { + #[allow(unused_unsafe)] + unsafe { + let baseline = ExternalGetBaseline( + convert_node_ref_to_ptr(node) as crate::ffi::NodePtr, + width.to_f32(), + height.to_f32(), + ); + Some(Len::from_f32(baseline)) + } +} + +pub(crate) fn dirty_callback_impl(node: &Node) { + #[allow(unused_unsafe)] + unsafe { + ExternalDirtyCallback(convert_node_ref_to_ptr(node) as crate::ffi::NodePtr) + }; +} + +#[allow(clippy::too_many_arguments)] +pub(crate) fn measure_impl( + node: &Node, + max_width: Len, + width_mode: MeasureMode, + max_height: Len, + height_mode: MeasureMode, + min_width: Len, + min_height: Len, + max_content_width: Len, + max_content_height: Len, +) -> Option> { + #[allow(unused_unsafe)] + unsafe { + let size = ExternalMeasure( + convert_node_ref_to_ptr(node) as crate::ffi::NodePtr, + to_f32_with_max_to_infinity(max_width), + width_mode, + to_f32_with_max_to_infinity(max_height), + height_mode, + min_width.to_f32(), + min_height.to_f32(), + to_f32_with_max_to_infinity(max_content_width), + to_f32_with_max_to_infinity(max_content_height), + ); + Some(Size::new( + Len::from_f32(size.width), + Len::from_f32(size.height), + )) + } +} + +pub(crate) fn resolve_calc_impl(node: &Node, calc_handle: i32, owner: Len) -> Len { + #[allow(unused_unsafe)] + unsafe { + Len::from_f32(ExternalResolveCalc( + convert_node_ref_to_ptr(node) as crate::ffi::NodePtr, + calc_handle, + owner.to_f32(), + )) + } +} + +#[cfg(debug_assertions)] +pub mod mock_external { + + #[derive(Debug, Default, Clone)] + pub struct TextInfo { + pub(crate) font_size: f32, + // raw_text: String + pub(crate) text_len: usize, + } + + impl TextInfo { + pub(crate) fn measure( + &self, + min_width: f32, + min_height: f32, + max_width: f32, + max_height: f32, + max_content_width: f32, + max_content_height: f32, + ) -> (f32, f32) { + let text_len = self.text_len; + let text_width = self.font_size * text_len as f32; + let max_w = max_width.min(max_content_width); + let max_h = max_height.min(max_content_height); + let measured_width; + let measured_height; + if text_width <= max_w { + // single line + measured_width = text_width; + measured_height = self.font_size; + } else { + // multi line + let mut row_count = (max_w.to_f32() / self.font_size).floor(); + if row_count < 1. { + row_count = 1.; + } + let col_count = (text_len as f32 / row_count).ceil(); + measured_width = (row_count * self.font_size) as i32 as f32; + measured_height = (col_count * self.font_size) as i32 as f32; + } + println!( + "text_info: {self:?}, width: {min_width:?} ~ {max_width:?}, height: {min_height:?} ~ {max_height:?}, max_content_width: {max_content_width:?}, max_content_height: {max_content_height:?}, measured_width: {measured_width:?}, measured_height: {measured_height:?}" + ); + (measured_width, measured_height.min(max_h)) + } + } + + pub struct TextInfoBuilder(TextInfo); + + impl Default for TextInfoBuilder { + fn default() -> Self { + Self::new() + } + } + + impl TextInfoBuilder { + pub fn new() -> Self { + Self(TextInfo { + font_size: 16., + // raw_text: String::new(), + text_len: 0, + }) + } + pub fn with_text_len(mut self, text_len: usize) -> Self { + self.0.text_len = text_len; + self + } + pub fn with_font_size(mut self, font_size: f32) -> Self { + self.0.font_size = font_size; + self + } + pub fn set_font_size(&mut self, font_size: f32) { + self.0.font_size = font_size; + } + pub fn build(self) -> TextInfo { + self.0 + } + } + use std::{cell::RefCell, collections::HashMap}; + + use float_pigment_css::length_num::LengthNum; + + use crate::{ffi::MeasureMode, get_ref_from_node_ptr, Node, NodePtr}; + + pub enum MeasureType { + Text(TextInfo), + SpecifiedSize((f32, f32)), + #[allow(clippy::type_complexity)] + Custom( + Box< + dyn Fn(&Node, f32, MeasureMode, f32, MeasureMode, f32, f32, f32, f32) -> (f32, f32), + >, + ), + } + + thread_local! { + static COLLECTION: RefCell> = RefCell::new(HashMap::new()); + } + + pub fn set_node_measure_type(node: *mut Node, measure_type: MeasureType) { + COLLECTION.with(|collection| { + let mut collection = collection.borrow_mut(); + collection.insert(node, measure_type); + }); + } + + #[allow(non_snake_case)] + pub(crate) fn ExternalGetBaseline(_node: *mut (), _width: f32, height: f32) -> f32 { + let mut res = 0.; + COLLECTION.with(|collection| { + let collection = collection.borrow(); + if let Some(measure_type) = collection.get(&(_node as *mut Node)) { + match measure_type { + MeasureType::Text(text_info) => { + res = text_info.font_size; + } + _ => res = height, + } + } + }); + res + } + + #[allow(clippy::too_many_arguments)] + #[allow(non_snake_case)] + pub(crate) fn ExternalMeasure( + node: *mut (), + max_width: f32, + _width_mode: MeasureMode, + max_height: f32, + _height_mode: MeasureMode, + min_width: f32, + min_height: f32, + max_content_width: f32, + max_content_height: f32, + ) -> crate::ffi::Size { + let mut res = crate::ffi::Size { + width: 0., + height: 0., + }; + COLLECTION.with(|collection| { + let collection = collection.borrow(); + if let Some(measure_type) = collection.get(&(node as *mut Node)) { + match measure_type { + MeasureType::Text(text_info) => { + let (width, height) = text_info.measure( + min_width, + min_height, + max_width, + max_height, + max_content_width, + max_content_height, + ); + res = crate::ffi::Size { width, height }; + } + MeasureType::SpecifiedSize((width, height)) => { + res = crate::ffi::Size { + width: *width, + height: *height, + }; + } + MeasureType::Custom(func) => { + let (width, height) = func( + unsafe { get_ref_from_node_ptr(node as NodePtr) }, + max_width, + _width_mode, + max_height, + _height_mode, + min_width, + min_height, + max_content_width, + max_content_height, + ); + res = crate::ffi::Size { width, height } + } + } + } + }); + res + } + + #[allow(non_snake_case)] + pub(crate) fn ExternalResolveCalc(_node: *mut (), _calc_handle: i32, _owner: f32) -> f32 { + todo!() + } + + #[allow(non_snake_case)] + pub(crate) fn ExternalDirtyCallback(node: *mut ()) { + use crate::DumpNode; + println!("trigger dirty callback of Node: {}", unsafe { + get_ref_from_node_ptr(node as *mut Node).dump_to_html( + crate::DumpOptions { + recursive: false, + layout: true, + style: crate::DumpStyleMode::None, + }, + 0, + ) + }); + } +} diff --git a/float-pigment-forest/src/ffi.rs b/float-pigment-forest/src/ffi.rs index 8277f53..6c53068 100644 --- a/float-pigment-forest/src/ffi.rs +++ b/float-pigment-forest/src/ffi.rs @@ -1,7 +1,6 @@ use crate::NodeType; use crate::{ - node::DumpNode, node::DumpOptions, node::DumpStyleMode, ChildOperation, Len, MeasureMode, Node, - StyleSetter, + node::DumpNode, node::DumpOptions, node::DumpStyleMode, ChildOperation, Len, Node, StyleSetter, }; use float_pigment_css::length_num::*; use float_pigment_css::property::PropertyValueWithGlobal; @@ -12,35 +11,31 @@ use float_pigment_css::typing::{ use float_pigment_layout::{DefLength, OptionNum}; use std::{ffi::CString, os::raw::c_char}; -pub type Width = f32; -pub type Height = f32; -pub type Baseline = f32; -pub type MeasureMinWidth = f32; -pub type MeasureMinHeight = f32; -pub type MeasureMaxWidth = f32; -pub type MeasureMaxHeight = f32; -pub type MeasureMaxContentWidth = f32; -pub type MeasureMaxContentHeight = f32; - -pub type BaselineFunc = unsafe extern "C" fn(NodePtr, Width, Height) -> Baseline; - -pub type MeasureFunc = unsafe extern "C" fn( - NodePtr, - MeasureMaxWidth, - MeasureMode, - MeasureMaxHeight, - MeasureMode, - MeasureMinWidth, - MeasureMinHeight, - MeasureMaxContentWidth, - MeasureMaxContentHeight, -) -> Size; - -pub type CalcHandle = i32; - -pub type ResolveCalc = unsafe extern "C" fn(CalcHandle, f32) -> f32; +#[cfg(not(debug_assertions))] +extern "C" { + pub fn ExternalDirtyCallback(node: NodePtr); + pub fn ExternalGetBaseline(node: NodePtr, width: f32, height: f32) -> f32; + pub fn ExternalMeasure( + node: NodePtr, + max_width: f32, + width_mode: MeasureMode, + max_height: f32, + width_mode: MeasureMode, + min_width: f32, + min_height: f32, + max_content_width: f32, + max_content_height: f32, + ) -> Size; + pub fn ExternalResolveCalc(node: NodePtr, calc_handle: i32, owner: f32) -> f32; +} -pub type DirtyCallback = unsafe extern "C" fn(NodePtr); +#[repr(C)] +#[derive(Copy, Clone)] +pub enum MeasureMode { + Undefined, + Exactly, + AtMost, +} #[repr(C)] pub struct Size { @@ -632,142 +627,61 @@ pub unsafe extern "C" fn NodeMarkDirtyAndPropagateToDescendants(node: NodePtr) { /// # Safety /// -/// Set the resolve calc function for a node instance. -/// -/// # Arguments -/// * `node` - Raw pointer to the Node instance -/// * `resolve_calc` - Resolve calc function -/// -/// # Example -/// -/// ```c -/// NodeSetResolveCalc(node, resolve_calc); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn NodeSetResolveCalc(node: NodePtr, resolve_calc: ResolveCalc) { - let node = &*(node as *mut Node); - node.set_resolve_calc(Some(Box::new(move |handle: i32, parent: Len| -> Len { - let parent_f32 = parent.to_f32(); - let ret = resolve_calc(handle, parent_f32); - Len::from_f32(ret) - }))); -} - -pub(crate) fn convert_len_max_to_infinity(v: Len) -> f32 { - if v == Len::MAX { - f32::INFINITY - } else { - v.to_f32() - } -} - -/// # Safety -/// -/// Set the measure function for a node instance. +/// Set the is measurable of a node instance. /// /// # Arguments /// * `node` - Raw pointer to the Node instance -/// * `measure_func` - Measure function +/// * `is_measurable` - Is measurable /// /// # Example /// /// ```c -/// NodeSetMeasureFunc(node, measure_func); +/// NodeSetIsMeasurable(node, is_measurable); /// ``` #[no_mangle] -pub unsafe extern "C" fn NodeSetMeasureFunc(node: NodePtr, measure_func: MeasureFunc) { +pub unsafe extern "C" fn NodeSetIsMeasurable(node: NodePtr, is_measurable: bool) { let node = &*(node as *mut Node); - node.set_measure_func(Some(Box::new( - move |node: *mut Node, - max_width: crate::node::MeasureMaxWidth, - width_mode: MeasureMode, - max_height: crate::node::MeasureMaxHeight, - height_mode: MeasureMode, - min_width: crate::node::MeasureMinWidth, - min_height: crate::node::MeasureMinHeight, - max_content_width: crate::node::MeasureMaxContentWidth, - max_content_height: crate::node::MeasureMaxContentHeight| - -> crate::node::Size { - measure_func( - node as NodePtr, - convert_len_max_to_infinity(max_width), - width_mode, - convert_len_max_to_infinity(max_height), - height_mode, - min_width.to_f32(), - min_height.to_f32(), - convert_len_max_to_infinity(max_content_width), - convert_len_max_to_infinity(max_content_height), - ) - .into() - }, - ))); + node.set_is_measurable(is_measurable); } /// # Safety /// -/// Clear the measure function for a node instance. -/// -/// # Arguments -/// * `node` - Raw pointer to the Node instance -/// -/// # Example -/// -/// ```c -/// NodeClearMeasureFunc(node); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn NodeClearMeasureFunc(node: NodePtr) { - let node = &*(node as *mut Node); - node.set_measure_func(None); -} - -/// # Safety -/// -/// Check if a node instance has a measure function. +/// Get the is measurable of a node instance. /// /// # Arguments /// * `node` - Raw pointer to the Node instance /// /// # Returns -/// * `bool` - True if the node has a measure function, false otherwise +/// * `bool` - Is measurable /// /// # Example /// /// ```c -/// NodeHasMeasureFunc(node); +/// NodeIsMeasurable(node); /// ``` #[no_mangle] -pub unsafe extern "C" fn NodeHasMeasureFunc(node: NodePtr) -> bool { +pub unsafe extern "C" fn NodeIsMeasurable(node: NodePtr) -> bool { let node = &*(node as *mut Node); - node.has_measure_func() + node.is_measurable() } /// # Safety /// -/// Set the baseline function for a node instance. +/// Set the has baseline of a node instance. /// /// # Arguments /// * `node` - Raw pointer to the Node instance -/// * `baseline_func` - Baseline function +/// * `has_baseline` - Has baseline /// /// # Example /// /// ```c -/// NodeSetBaselineFunc(node, baseline_func); +/// NodeSetHasBaseline(node, has_baseline); /// ``` #[no_mangle] -pub unsafe extern "C" fn NodeSetBaselineFunc(node: NodePtr, baseline_func: BaselineFunc) { +pub unsafe extern "C" fn NodeSetHasBaseline(node: NodePtr, has_baseline: bool) { let node = &*(node as *mut Node); - node.set_baseline_func(Some(Box::new( - move |node: *mut Node, width: Len, height: Len| -> Len { - Len::from_f32(baseline_func( - node as NodePtr, - width.to_f32(), - height.to_f32(), - )) - }, - ))); + node.set_has_baseline(has_baseline); } /// # Safety @@ -791,41 +705,21 @@ pub unsafe extern "C" fn NodeClearMeasureCache(node: NodePtr) { /// # Safety /// -/// Set the dirty callback for a node instance. -/// -/// # Arguments -/// * `node` - Raw pointer to the Node instance -/// * `dirty_cb` - Dirty callback -/// -/// # Example -/// -/// ```c -/// NodeSetDirtyCallback(node, dirty_cb); -/// ``` -#[no_mangle] -pub unsafe extern "C" fn NodeSetDirtyCallback(node: NodePtr, dirty_cb: DirtyCallback) { - let node = &*(node as *mut Node); - node.set_dirty_callback(Some(Box::new(move |node: *mut Node| { - dirty_cb(node as NodePtr) - }))) -} - -/// # Safety -/// -/// Clear the dirty callback for a node instance. +/// Set the should observe dirty of a node instance. /// /// # Arguments /// * `node` - Raw pointer to the Node instance +/// * `should_observe_dirty` - Should observe dirty /// /// # Example /// /// ```c -/// NodeClearDirtyCallback(node); +/// NodeSetShouldObserveDirty(node, should_observe_dirty); /// ``` #[no_mangle] -pub unsafe extern "C" fn NodeClearDirtyCallback(node: NodePtr) { +pub unsafe extern "C" fn NodeSetShouldObserveDirty(node: NodePtr, should_observe_dirty: bool) { let node = &*(node as *mut Node); - node.set_dirty_callback(None); + node.set_should_observe_dirty(should_observe_dirty); } /// # Safety diff --git a/float-pigment-forest/src/layout/layout_impl.rs b/float-pigment-forest/src/layout/layout_impl.rs index 33803bf..034dc7b 100644 --- a/float-pigment-forest/src/layout/layout_impl.rs +++ b/float-pigment-forest/src/layout/layout_impl.rs @@ -12,11 +12,12 @@ use float_pigment_layout::{ LayoutTreeNode, LayoutTreeVisitor, MeasureResult, OptionNum, OptionSize, Point, Size, Vector, }; -use crate::{convert_node_ref_to_ptr, Length}; +use crate::ffi::MeasureMode; +use crate::Length; use crate::{ env::Env, node::{ChildOperation, Node}, - Len, MeasureMode, NodeType, + Len, NodeType, }; fn is_specified(x: Len) -> bool { @@ -53,15 +54,12 @@ impl LayoutTreeNode for Node { custom: &Self::LengthCustom, owner: Self::Length, ) -> Self::Length { - if let Some(func) = self.resolve_calc() { - return func(*custom, owner); - }; - Len::zero() + self.resolve_calc(*custom, owner) } #[inline] fn should_measure(&self, _env: &mut Self::Env) -> bool { - self.has_measure_func() + self.is_measurable() } #[inline] @@ -101,72 +99,69 @@ impl LayoutTreeNode for Node { } } if !skip_measure { - if let Some(func) = unsafe { self.measure_func() } { - let mut width_measure_mode = MeasureMode::AtMost; - let mut height_measure_mode = MeasureMode::AtMost; - let (min_width, max_width) = if let Some(req_size_width) = req_size.width.val() { - let min_width = req_size_width; - let max_width = req_size_width; - width_measure_mode = MeasureMode::Exactly; - (min_width, max_width) + let mut width_measure_mode = MeasureMode::AtMost; + let mut height_measure_mode = MeasureMode::AtMost; + let (min_width, max_width) = if let Some(req_size_width) = req_size.width.val() { + let min_width = req_size_width; + let max_width = req_size_width; + width_measure_mode = MeasureMode::Exactly; + (min_width, max_width) + } else { + let min_width = if !is_specified(min.width) { + Len::zero() } else { - let min_width = if !is_specified(min.width) { - Len::zero() - } else { - min.width - }; - let max_width = max.width; - (min_width, max_width) + min.width }; - let (min_height, max_height) = if let Some(req_size_height) = req_size.height.val() - { - let min_height = req_size_height; - let max_height = req_size_height; - height_measure_mode = MeasureMode::Exactly; - (min_height, max_height) + let max_width = max.width; + (min_width, max_width) + }; + let (min_height, max_height) = if let Some(req_size_height) = req_size.height.val() { + let min_height = req_size_height; + let max_height = req_size_height; + height_measure_mode = MeasureMode::Exactly; + (min_height, max_height) + } else { + let min_height = if !is_specified(min.height) { + Len::zero() } else { - let min_height = if !is_specified(min.height) { - Len::zero() - } else { - min.height - }; - let max_height = max.height; - (min_height, max_height) + min.height }; - let mut size_from_cache = false; - if self.node_type() == NodeType::Text { - if let Some(cache) = unsafe { self.measure_cache() }.as_mut() { - if let Some(size_cache) = cache.get(&( - OptionSize::new( - OptionNum::some(min_width).to_hashable(), - OptionNum::some(min_height).to_hashable(), - ), - OptionSize::new( - OptionNum::some(max_width).to_hashable(), - OptionNum::some(max_height).to_hashable(), - ), - OptionSize::new( - max_content.width.to_hashable(), - max_content.height.to_hashable(), - ), - )) { - size = *size_cache; - size_from_cache = true; - } + let max_height = max.height; + (min_height, max_height) + }; + let mut size_from_cache = false; + if self.node_type() == NodeType::Text { + if let Some(cache) = unsafe { self.measure_cache() }.as_mut() { + if let Some(size_cache) = cache.get(&( + OptionSize::new( + OptionNum::some(min_width).to_hashable(), + OptionNum::some(min_height).to_hashable(), + ), + OptionSize::new( + OptionNum::some(max_width).to_hashable(), + OptionNum::some(max_height).to_hashable(), + ), + OptionSize::new( + max_content.width.to_hashable(), + max_content.height.to_hashable(), + ), + )) { + size = *size_cache; + size_from_cache = true; } } - if !size_from_cache { - let measure_size = func( - convert_node_ref_to_ptr(self), - max_width, - width_measure_mode, - max_height, - height_measure_mode, - min_width, - min_height, - max_content.width.unwrap_or(max_width), - max_content.height.unwrap_or(max_height), - ); + } + if !size_from_cache { + if let Some(measure_size) = self.measure( + max_width, + width_measure_mode, + max_height, + height_measure_mode, + min_width, + min_height, + max_content.width.unwrap_or(max_width), + max_content.height.unwrap_or(max_height), + ) { let width = if is_specified(measure_size.width) { measure_size.width } else { @@ -204,7 +199,7 @@ impl LayoutTreeNode for Node { } } } - }; + } } let mut baseline = size.to_vector(); let mut baseline_from_cache = false; @@ -216,12 +211,11 @@ impl LayoutTreeNode for Node { } } } - if !baseline_from_cache { - if let Some(func) = unsafe { self.baseline_func() } { - let ret = func(convert_node_ref_to_ptr(self), size.width, size.height); - baseline = Vector::new(Len::zero(), ret); + if !baseline_from_cache && self.has_baseline() { + if let Some(res) = self.get_baseline(size.width, size.height) { + baseline = Vector::new(Len::zero(), res); if let Some(cache) = unsafe { self.baseline_cache() }.as_mut() { - cache.put(Size::new(size.width, size.height), ret); + cache.put(Size::new(size.width, size.height), res); } } } diff --git a/float-pigment-forest/src/lib.rs b/float-pigment-forest/src/lib.rs index 32423f4..6388bca 100644 --- a/float-pigment-forest/src/lib.rs +++ b/float-pigment-forest/src/lib.rs @@ -1,4 +1,5 @@ pub(crate) mod env; +pub mod external; pub mod ffi; pub mod layout; pub mod node; diff --git a/float-pigment-forest/src/node.rs b/float-pigment-forest/src/node.rs index 62b5f82..3cc3b97 100644 --- a/float-pigment-forest/src/node.rs +++ b/float-pigment-forest/src/node.rs @@ -1,3 +1,5 @@ +use crate::external::{dirty_callback_impl, get_baseline_impl, measure_impl, resolve_calc_impl}; +use crate::ffi::MeasureMode; use crate::{env::Env, layout::LayoutPosition, style::StyleManager}; use float_pigment_css::typing::{ AlignContent, AlignItems, AlignSelf, BoxSizing, Direction, FlexDirection, FlexWrap, @@ -21,7 +23,7 @@ pub type NodePtr = *mut Node; #[inline(always)] pub fn convert_node_ref_to_ptr(node: &Node) -> NodePtr { - node as *const Node as *mut Node + node as *const Node as NodePtr } #[inline(always)] @@ -32,29 +34,6 @@ pub unsafe fn get_ref_from_node_ptr(node_ptr: NodePtr) -> &'static Node { pub type ExternalHostPtr = *mut (); -pub(crate) type MeasureMinWidth = Len; -pub(crate) type MeasureMinHeight = Len; -pub(crate) type MeasureMaxWidth = Len; -pub(crate) type MeasureMaxHeight = Len; -pub(crate) type MeasureMaxContentWidth = Len; -pub(crate) type MeasureMaxContentHeight = Len; - -pub(crate) type MeasureFn = dyn Fn( - NodePtr, - MeasureMaxWidth, - MeasureMode, - MeasureMaxHeight, - MeasureMode, - MeasureMinWidth, - MeasureMinHeight, - MeasureMaxContentWidth, - MeasureMaxContentHeight, -) -> Size; - -pub(crate) type BaselineFn = dyn Fn(NodePtr, L, L) -> L; -pub(crate) type ResolveCalcFn = dyn Fn(i32, L) -> L; -pub(crate) type DirtyCallbackFn = dyn Fn(NodePtr); - pub(crate) type MeasureCacheKeyMinSize = OptionSize<::Hashable>; pub(crate) type MeasureCacheKeyMaxSize = OptionSize<::Hashable>; pub(crate) type MeasureCacheKeyMaxContent = OptionSize<::Hashable>; @@ -70,14 +49,6 @@ pub(crate) type BaselineCache = LruCache::Hashable>, Len const CACHE_SIZE: usize = 3; -#[repr(C)] -#[derive(Copy, Clone)] -pub enum MeasureMode { - Undefined, - Exactly, - AtMost, -} - #[derive(Copy, Clone, Debug)] pub struct DumpOptions { pub recursive: bool, @@ -132,7 +103,7 @@ impl DumpNode for Node { Display::Grid => "Grid".into(), Display::InlineFlex => "InlineFlex".into(), }; - if self.has_measure_func() { + if self.is_measurable() { tag = format!("Measurable{tag}"); } if let Some(children) = children { @@ -186,6 +157,9 @@ pub enum NodeType { pub struct Node { node_type: Cell, is_dirty: Cell, + should_observe_dirty: Cell, + has_baseline: Cell, + is_measurable: Cell, external_host: Cell, parent: Cell, children: RefCell>, @@ -193,10 +167,6 @@ pub struct Node { pub(crate) layout_node: LayoutNode, measure_cache: UnsafeCell>>, baseline_cache: UnsafeCell>>, - baseline_func: UnsafeCell>>>, - measure_func: UnsafeCell>>>, - resolve_calc: UnsafeCell>>>, - dirty_callback: UnsafeCell>>, } impl Node { @@ -209,12 +179,11 @@ impl Node { style_manager: RefCell::new(StyleManager::new()), layout_node: LayoutNode::new(), is_dirty: Cell::new(true), - baseline_func: UnsafeCell::new(None), - measure_func: UnsafeCell::new(None), - resolve_calc: UnsafeCell::new(None), - dirty_callback: UnsafeCell::new(None), measure_cache: UnsafeCell::new(None), baseline_cache: UnsafeCell::new(None), + should_observe_dirty: Cell::new(false), + has_baseline: Cell::new(false), + is_measurable: Cell::new(false), } } pub fn new_typed(node_type: NodeType) -> Self { @@ -281,6 +250,7 @@ impl Node { } } + #[allow(clippy::mut_from_ref)] #[inline(always)] pub(crate) unsafe fn measure_cache(&self) -> Option<&mut MeasureCache> { if self.node_type() != NodeType::Text { @@ -295,6 +265,7 @@ impl Node { } } + #[allow(clippy::mut_from_ref)] #[inline(always)] pub(crate) unsafe fn baseline_cache(&self) -> Option<&mut BaselineCache> { if self.node_type() != NodeType::Text { @@ -311,51 +282,68 @@ impl Node { pub(crate) fn node_type(&self) -> NodeType { self.node_type.get() } - pub(crate) unsafe fn baseline_func(&self) -> Option<&BaselineFn> { - (*self.baseline_func.get()).as_deref() - } - pub unsafe fn set_baseline_func(&self, baseline_func: Option>>) { - drop(std::mem::replace( - &mut *self.baseline_func.get(), - baseline_func, - )); - } - pub unsafe fn has_baseline_func(&self) -> bool { - (*self.baseline_func.get()).is_some() + pub(crate) fn has_baseline(&self) -> bool { + self.has_baseline.get() } - pub(crate) unsafe fn measure_func(&self) -> Option<&MeasureFn> { - (*self.measure_func.get()).as_deref() + + pub fn set_has_baseline(&self, has_baseline: bool) { + self.has_baseline.set(has_baseline); } - pub fn set_measure_func(&self, measure_func: Option>>) { - drop(std::mem::replace( - unsafe { &mut *self.measure_func.get() }, - measure_func, - )); + + pub(crate) fn get_baseline(&self, width: Len, height: Len) -> Option { + if !self.has_baseline() { + return None; + } + get_baseline_impl(self, width, height) } - pub fn has_measure_func(&self) -> bool { - unsafe { (*self.measure_func.get()).is_some() } + + pub fn set_is_measurable(&self, is_measurable: bool) { + self.is_measurable.set(is_measurable); } - pub(crate) fn resolve_calc(&self) -> Option<&ResolveCalcFn> { - unsafe { (*self.resolve_calc.get()).as_deref() } + + pub fn is_measurable(&self) -> bool { + self.is_measurable.get() } - pub fn set_resolve_calc(&self, resolve_calc: Option>>) { - drop(std::mem::replace( - unsafe { &mut *self.resolve_calc.get() }, - resolve_calc, - )) + + #[allow(clippy::too_many_arguments)] + pub(crate) fn measure( + &self, + max_width: Len, + width_mode: MeasureMode, + max_height: Len, + height_mode: MeasureMode, + min_width: Len, + min_height: Len, + max_content_width: Len, + max_content_height: Len, + ) -> Option> { + if !self.is_measurable() { + return None; + } + measure_impl( + self, + max_width, + width_mode, + max_height, + height_mode, + min_width, + min_height, + max_content_width, + max_content_height, + ) } - pub fn set_dirty_callback(&self, dirty_callback: Option>) { - drop(std::mem::replace( - unsafe { &mut *self.dirty_callback.get() }, - dirty_callback, - )); + + pub fn set_should_observe_dirty(&self, should_observe_dirty: bool) { + self.should_observe_dirty.set(should_observe_dirty); } - pub fn has_dirty_callback(&self) -> bool { - unsafe { (*self.dirty_callback.get()).is_some() } + pub fn should_observe_dirty(&self) -> bool { + self.should_observe_dirty.get() } - pub(crate) fn dirty_callback(&self) -> Option<&DirtyCallbackFn> { - unsafe { (*self.dirty_callback.get()).as_deref() } + + pub fn resolve_calc(&self, calc_handle: i32, owner: Len) -> Len { + resolve_calc_impl(self, calc_handle, owner) } + pub fn external_host(&self) -> Option { if self.external_host.get().is_null() { None @@ -393,8 +381,8 @@ impl Node { self.clear_measure_cache(); self.clear_baseline_cache(); } - if let Some(dirty_callback) = self.dirty_callback() { - dirty_callback(convert_node_ref_to_ptr(self)) + if self.should_observe_dirty() { + dirty_callback_impl(self) } self.layout_node.mark_dirty(self); } diff --git a/float-pigment-forest/tests/custom/css_flexbox/align_items.rs b/float-pigment-forest/tests/custom/css_flexbox/align_items.rs index c7e8d7e..c3bd225 100644 --- a/float-pigment-forest/tests/custom/css_flexbox/align_items.rs +++ b/float-pigment-forest/tests/custom/css_flexbox/align_items.rs @@ -175,7 +175,7 @@ fn align_items_baseline_margin_top() { r#"
xxx
-
xxx
+
x x x
xxx
"# diff --git a/float-pigment-forest/tests/custom/css_inline/inline.rs b/float-pigment-forest/tests/custom/css_inline/inline.rs index e792c28..6c81995 100644 --- a/float-pigment-forest/tests/custom/css_inline/inline.rs +++ b/float-pigment-forest/tests/custom/css_inline/inline.rs @@ -341,9 +341,11 @@ pub fn measurable_inline_block_with_padding() { child.set_display(Display::InlineBlock); child.set_padding_left(DefLength::Points(Len::from_f32(12.))); child.set_padding_right(DefLength::Points(Len::from_f32(12.))); - child.set_measure_func(Some(Box::new(|_, _, _, _, _, _, _, _, _| { - Size::new(Len::from_f32(20.), Len::from_f32(20.)) - }))); + child.set_is_measurable(true); + set_node_measure_type( + convert_node_ref_to_ptr(child), + MeasureType::SpecifiedSize((20., 20.)), + ); container.append_child(convert_node_ref_to_ptr(child)); container.layout( OptionSize::new( @@ -368,9 +370,11 @@ pub fn measurable_inline_block_with_margin() { child.set_display(Display::InlineBlock); child.set_margin_left(DefLength::Points(Len::from_f32(12.))); child.set_margin_right(DefLength::Points(Len::from_f32(12.))); - child.set_measure_func(Some(Box::new(|_, _, _, _, _, _, _, _, _| { - Size::new(Len::from_f32(20.), Len::from_f32(20.)) - }))); + set_node_measure_type( + convert_node_ref_to_ptr(child), + MeasureType::SpecifiedSize((20., 20.)), + ); + child.set_is_measurable(true); container.append_child(convert_node_ref_to_ptr(child)); container.layout( OptionSize::new( @@ -394,9 +398,11 @@ pub fn measurable_inline_block_with_margin_2() { child.set_display(Display::InlineBlock); child.set_margin_left(DefLength::Points(Len::from_f32(12.))); child.set_margin_right(DefLength::Points(Len::from_f32(12.))); - child.set_measure_func(Some(Box::new(|_, _, _, _, _, _, _, _, _| { - Size::new(Len::from_f32(20.), Len::from_f32(20.)) - }))); + child.set_is_measurable(true); + set_node_measure_type( + convert_node_ref_to_ptr(child), + MeasureType::SpecifiedSize((20., 20.)), + ); container.append_child(convert_node_ref_to_ptr(child)); let child_b = as_ref(Node::new_ptr()); child_b.set_width(DefLength::Points(Len::from_f32(25.))); @@ -404,9 +410,11 @@ pub fn measurable_inline_block_with_margin_2() { child_b.set_display(Display::InlineBlock); child_b.set_margin_left(DefLength::Points(Len::from_f32(12.))); child_b.set_margin_right(DefLength::Points(Len::from_f32(12.))); - child_b.set_measure_func(Some(Box::new(|_, _, _, _, _, _, _, _, _| { - Size::new(Len::from_f32(25.), Len::from_f32(25.)) - }))); + child_b.set_is_measurable(true); + set_node_measure_type( + convert_node_ref_to_ptr(child_b), + MeasureType::SpecifiedSize((25., 25.)), + ); container.append_child(convert_node_ref_to_ptr(child_b)); container.layout( OptionSize::new( diff --git a/float-pigment-forest/tests/mod.rs b/float-pigment-forest/tests/mod.rs index 3745aff..9bdced5 100644 --- a/float-pigment-forest/tests/mod.rs +++ b/float-pigment-forest/tests/mod.rs @@ -11,7 +11,12 @@ use float_pigment_css::{ typing::{AspectRatio, Display, Gap}, }; pub use float_pigment_forest::Len; -use float_pigment_forest::{layout::LayoutPosition, node::Length, *}; +use float_pigment_forest::{ + external::mock_external::{set_node_measure_type, MeasureType, TextInfo, TextInfoBuilder}, + layout::LayoutPosition, + node::Length, + *, +}; use float_pigment_layout::DefLength; use float_pigment_mlp::{ context::{Context, Parse}, @@ -107,75 +112,6 @@ fn is_block_tag(tag: &str) -> bool { fn is_measure_text_slot(tag: &str) -> bool { tag == "text-slot" } -#[derive(Debug, Default, Clone)] -struct TextInfo { - font_size: f32, - // raw_text: String - text_len: usize, -} - -impl TextInfo { - fn measure( - &self, - min_width: Len, - min_height: Len, - max_width: Len, - max_height: Len, - max_content_width: Len, - max_content_height: Len, - ) -> Size { - let text_len = self.text_len; - let text_width = self.font_size * text_len as f32; - let max_w = max_width.min(max_content_width); - let max_h = max_height.min(max_content_height); - let measured_width; - let measured_height; - if text_width <= max_w { - // single line - measured_width = Len::from_f32(text_width); - measured_height = Len::from_f32(self.font_size); - } else { - // multi line - let mut row_count = (max_w.to_f32() / self.font_size).floor(); - if row_count < 1. { - row_count = 1.; - } - let col_count = (text_len as f32 / row_count).ceil(); - measured_width = Len::from_f32((row_count * self.font_size) as i32 as f32); - measured_height = Len::from_f32((col_count * self.font_size) as i32 as f32); - } - println!( - "text_info: {self:?}, width: {min_width:?} ~ {max_width:?}, height: {min_height:?} ~ {max_height:?}, max_content_width: {max_content_width:?}, max_content_height: {max_content_height:?}, measured_width: {measured_width:?}, measured_height: {measured_height:?}", - ); - Size::new(measured_width, measured_height.min(max_h)) - } -} - -struct TextInfoBuilder(TextInfo); - -impl TextInfoBuilder { - fn new() -> Self { - Self(TextInfo { - font_size: 16., - // raw_text: String::new(), - text_len: 0, - }) - } - fn with_text_len(mut self, text_len: usize) -> Self { - self.0.text_len = text_len; - self - } - fn with_font_size(mut self, font_size: f32) -> Self { - self.0.font_size = font_size; - self - } - fn set_font_size(&mut self, font_size: f32) { - self.0.font_size = font_size; - } - fn build(self) -> TextInfo { - self.0 - } -} #[inline(always)] fn convert_font_size_to_px(font_size: float_pigment_css::typing::Length) -> f32 { @@ -187,31 +123,13 @@ fn convert_font_size_to_px(font_size: float_pigment_css::typing::Length) -> f32 #[inline(always)] fn prepare_measure_node(node: *mut Node, text_info: TextInfo) { - let node = unsafe { &mut *node }; - node.set_measure_func(Some(Box::new( - move |_, - max_width, - _, - max_height, - _, - min_width, - min_height, - max_content_width, - max_content_height| { - text_info.measure( - min_width, - min_height, - max_width, - max_height, - max_content_width, - max_content_height, - ) - }, - ))); + set_node_measure_type(node, MeasureType::Text(text_info)); + unsafe { - node.set_display(Display::Inline); - node.set_baseline_func(Some(Box::new(|_, _, _| Len::from_f32(16.)))); - node.set_node_type(float_pigment_forest::NodeType::Text); + (*node).set_display(Display::Inline); + (*node).set_is_measurable(true); + (*node).set_has_baseline(true); + (*node).set_node_type(float_pigment_forest::NodeType::Text); } } diff --git a/float-pigment/Cargo.toml b/float-pigment/Cargo.toml index 31a22b7..86faef3 100644 --- a/float-pigment/Cargo.toml +++ b/float-pigment/Cargo.toml @@ -12,7 +12,7 @@ repository.workspace = true [lib] name = "float_pigment" -crate-type = ["staticlib", "cdylib", "rlib"] +crate-type = ["staticlib", "rlib"] [[bin]] name = "float_pigment_cpp_binding_gen_tool"