use crate::core::ElementId;
use crate::layout::Layout;
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, Default)]
pub struct Dimensions {
pub width: f32,
pub height: f32,
}
impl From<Layout> for Dimensions {
fn from(layout: Layout) -> Self {
Self {
width: layout.width,
height: layout.height,
}
}
}
#[derive(Clone, Default)]
pub struct MeasureContext {
layouts: HashMap<ElementId, Layout>,
}
impl MeasureContext {
pub fn new() -> Self {
Self {
layouts: HashMap::new(),
}
}
pub fn set_layouts(&mut self, layouts: HashMap<ElementId, Layout>) {
self.layouts = layouts;
}
pub fn measure(&self, element_id: ElementId) -> Option<Dimensions> {
self.layouts
.get(&element_id)
.map(|layout| Dimensions::from(*layout))
}
}
pub fn measure_element(element_id: ElementId) -> Option<Dimensions> {
if let Some(ctx) = crate::runtime::current_runtime() {
ctx.borrow()
.get_measurement_dims(element_id)
.map(|(w, h)| Dimensions {
width: w,
height: h,
})
} else {
None
}
}
pub fn measure_element_by_key(key: &str) -> Option<Dimensions> {
if let Some(ctx) = crate::runtime::current_runtime() {
ctx.borrow()
.get_measurement_by_key_dims(key)
.map(|(w, h)| Dimensions {
width: w,
height: h,
})
} else {
None
}
}
pub fn use_measure() -> (MeasureRef, impl Fn() -> Option<Dimensions>) {
use crate::hooks::use_signal;
let element_id = use_signal(|| None::<ElementId>);
let element_key = use_signal(|| None::<String>);
let element_id_clone = element_id.clone();
let element_key_clone = element_key.clone();
let measure_ref = MeasureRef {
element_id,
element_key,
};
let get_dimensions = move || {
if let Some(id) = element_id_clone.get()
&& let Some(dims) = measure_element(id)
{
return Some(dims);
}
element_key_clone
.get()
.as_deref()
.and_then(measure_element_by_key)
};
(measure_ref, get_dimensions)
}
#[derive(Clone)]
pub struct MeasureRef {
element_id: crate::hooks::Signal<Option<ElementId>>,
element_key: crate::hooks::Signal<Option<String>>,
}
impl MeasureRef {
pub fn set(&self, id: ElementId) {
self.element_id.set(Some(id));
}
pub fn get(&self) -> Option<ElementId> {
self.element_id.get()
}
pub fn set_key(&self, key: impl Into<String>) {
self.element_key.set(Some(key.into()));
}
pub fn get_key(&self) -> Option<String> {
self.element_key.get()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::{Element, ElementType};
use crate::layout::LayoutEngine;
#[test]
fn test_dimensions_from_layout() {
let layout = Layout {
x: 0.0,
y: 0.0,
width: 100.0,
height: 50.0,
};
let dims = Dimensions::from(layout);
assert_eq!(dims.width, 100.0);
assert_eq!(dims.height, 50.0);
}
#[test]
fn test_measure_context() {
let mut element = Element::new(ElementType::Box);
element.style.width = crate::core::Dimension::Points(80.0);
element.style.height = crate::core::Dimension::Points(24.0);
let mut engine = LayoutEngine::new();
engine.compute(&element, 100, 100);
let mut ctx = MeasureContext::new();
ctx.set_layouts(engine.get_all_layouts());
let dims = ctx.measure(element.id);
assert!(dims.is_some());
let dims = dims.unwrap();
assert_eq!(dims.width, 80.0);
assert_eq!(dims.height, 24.0);
}
#[test]
fn test_measure_element_by_key_with_runtime() {
use crate::runtime::{RuntimeContext, set_current_runtime};
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
let ctx = Rc::new(RefCell::new(RuntimeContext::new()));
let mut keyed = HashMap::new();
keyed.insert(
"sidebar".to_string(),
Layout {
x: 0.0,
y: 0.0,
width: 30.0,
height: 10.0,
},
);
ctx.borrow_mut()
.set_measure_layouts_with_keys(HashMap::new(), keyed);
set_current_runtime(Some(ctx));
let dims = measure_element_by_key("sidebar");
assert_eq!(dims.map(|d| (d.width, d.height)), Some((30.0, 10.0)));
set_current_runtime(None);
}
}