Quellcode durchsuchen

Working node alignment and node margins.

Kestrel vor 3 Monaten
Ursprung
Commit
1db569b8bd

+ 9 - 2
examples/table_layout.rs

@@ -1,5 +1,6 @@
+use kahlo::math::PixelSideOffsets;
 use patina::{
-    layout::{TableCell, VerticalAlignment},
+    layout::{HorizontalAlignment, TableCell, VerticalAlignment},
     prelude::*,
     ui::{UIControlMsg, UIHandle},
     widget,
@@ -19,9 +20,11 @@ impl TableWindow {
                     // .framed()
                     .with_table_cell(TableCell::new(0, 0))
                     .boxed(),
-                widget::Label::new_with_text(uih, "x0y1")
+                widget::Label::new_with_text(uih, "x0y1 [left aligned]")
+                    .with_halign(HorizontalAlignment::Left)
                     .with_valign(VerticalAlignment::Centre)
                     .framed()
+                    .with_margins(PixelSideOffsets::new_all_same(15))
                     .with_table_cell(TableCell::new(0, 1))
                     .boxed(),
                 widget::Label::new_with_text(uih, "x1y1")
@@ -51,6 +54,10 @@ impl Component for TableWindow {
     type Msg = ();
 
     fn process(&mut self, _msg: Self::Msg) -> Vec<Self::ParentMsg> {
+        let mut out = String::new();
+        patina::layout::dump_node_tree(self.root.layout_node(), &mut out);
+        std::fs::write("patina-table-layout-example.layout_tree", out)
+            .expect("couldn't dump node tree");
         vec![UIControlMsg::Terminate]
     }
 }

+ 28 - 13
src/layout.rs

@@ -102,6 +102,9 @@ impl SizePolicy {
     }
 }
 
+pub struct SizePolicyTag;
+pub type SizePolicy2D = euclid::Size2D<SizePolicy, SizePolicyTag>;
+
 impl std::ops::Add<Self> for SizePolicy {
     type Output = Self;
     fn add(self, rhs: Self) -> Self::Output {
@@ -155,6 +158,7 @@ pub enum NodeBehaviour {
 #[derive(Clone)]
 pub enum ChildArrangement {
     Custom(std::rc::Rc<dyn arr::ArrangementCalculator>),
+    Container,
     Column,
     Row,
     Table,
@@ -164,6 +168,7 @@ impl std::fmt::Debug for ChildArrangement {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         match self {
             Self::Custom(_) => f.write_str("ChildArrangement::Custom"),
+            Self::Container => f.write_str("ChildArrangement::Container"),
             Self::Column => f.write_str("ChildArrangement::Column"),
             Self::Row => f.write_str("ChildArrangement::Row"),
             Self::Table => f.write_str("ChildArrangement::Table"),
@@ -176,6 +181,7 @@ impl Deref for ChildArrangement {
     fn deref(&self) -> &Self::Target {
         match self {
             Self::Custom(calc) => calc.as_ref(),
+            Self::Container => &arr::ContainerArrangement,
             Self::Column => &arr::LineArrangement::Column,
             Self::Row => &arr::LineArrangement::Row,
             Self::Table => &arr::TableArrangement,
@@ -245,6 +251,10 @@ impl LayoutNode {
         &self.cache
     }
 
+    fn with_state<R, F: FnOnce(&mut NodeState) -> R>(&self, f: F) -> Option<R> {
+        self.cache.with_state(self.cache_key, f)
+    }
+
     pub fn relayout(&self) {
         self.cache.update_queue.borrow_mut().push(self.cache_key);
     }
@@ -260,21 +270,17 @@ impl LayoutNode {
     }
 
     pub fn render_area(&self) -> Option<PixelBox> {
-        self.cache
-            .with_state(self.cache_key, |ns| ns.area)
-            .flatten()
-            .map(|pb| pb.inner_box(self.margin))
+        self.with_state(|ns| ns.area).flatten()
     }
 
     /// Checks if node needs to be rerendered, clearing the flag if so.
     pub fn render_check(&self) -> bool {
-        self.cache
-            .with_state(self.cache_key, |ns| {
-                let ret = ns.needs_render;
-                ns.needs_render = false;
-                ret
-            })
-            .unwrap_or(false)
+        self.with_state(|ns| {
+            let ret = ns.needs_render;
+            ns.needs_render = false;
+            ret
+        })
+        .unwrap_or(false)
     }
 
     pub fn render_needed(&self) {
@@ -349,6 +355,12 @@ impl LayoutNode {
     pub fn margin_mut(&mut self) -> &mut PixelSideOffsets {
         &mut self.margin
     }
+    pub fn margin_as_policy(&self) -> SizePolicy2D {
+        SizePolicy2D::new(
+            SizePolicy::fixed((self.margin.left + self.margin.right) as usize),
+            SizePolicy::fixed((self.margin.top + self.margin.bottom) as usize),
+        )
+    }
 
     pub fn table_cell(&self) -> &Option<TableCell> {
         &self.table_cell
@@ -400,13 +412,16 @@ fn dump_node_tree_helper(lna: LayoutNodeAccess, indent: usize, out: &mut String)
             lna.height_policy.desired,
             lna.height_policy.slack_weight,
             lna.behaviour,
-            lna.cache.with_state(lna.cache_key, |v| v.needs_update),
-            lna.cache.with_state(lna.cache_key, |v| v.needs_render),
+            lna.with_state(|v| v.needs_update),
+            lna.with_state(|v| v.needs_render),
         )
         .as_str(),
     );
     out.push_str(ind.as_str());
     out.push_str("        ");
+    out.push_str(format!("net policy:  {:?}\n", lna.with_state(|v| v.net_policy)).as_str());
+    out.push_str(ind.as_str());
+    out.push_str("        ");
     out.push_str(format!("render area: {:?}\n", lna.render_area()).as_str());
 
     for child in lna.child_iter() {

+ 24 - 25
src/layout/arr.rs

@@ -1,7 +1,7 @@
 use kahlo::math::{PixelBox, PixelSideOffsets};
 use std::ops::Add;
 
-use super::{LayoutNodeAccess, SizePolicy, HorizontalAlignment, VerticalAlignment};
+use super::{HorizontalAlignment, LayoutNodeAccess, SizePolicy, VerticalAlignment, SizePolicy2D};
 
 /// Child arrangement calculator trait.
 ///
@@ -10,50 +10,49 @@ use super::{LayoutNodeAccess, SizePolicy, HorizontalAlignment, VerticalAlignment
 /// node. The layout step then uses the computed constraints and size policies to compute a set of
 /// pixel-space rects.
 pub trait ArrangementCalculator {
-    fn arrange_step(&self, node: LayoutNodeAccess) -> (SizePolicy, SizePolicy);
+    fn arrange_step(&self, node: LayoutNodeAccess) -> SizePolicy2D;
     fn layout_step(&self, node: LayoutNodeAccess, inside: PixelBox);
 }
 
+mod container;
+pub use container::ContainerArrangement;
 mod line;
 pub use line::LineArrangement;
 mod table;
 pub use table::TableArrangement;
 
 fn do_align(node: &LayoutNodeAccess, mut inside: PixelBox) -> PixelBox {
-    let net_policy = node.cache.with_state(node.cache_key, |ns| ns.net_policy).expect("do_align invoked with node that has no net_policy");
+    let net_policy = node
+        .with_state(|ns| ns.net_policy)
+        .expect("do_align invoked with node that has no net_policy");
     println!("do_align | net: {net_policy:?}");
-    println!("         | alignments: {:?} {:?}", node.halign(), node.valign());
+    println!(
+        "         | alignments: {:?} {:?}",
+        node.halign(),
+        node.valign()
+    );
     println!("         | inside before: {inside:?}");
-    if net_policy.0.slack_weight == 0 {
-        let slack = (inside.width() - net_policy.0.desired as i32).max(0);
+    if net_policy.width.slack_weight == 0 {
+        let slack = (inside.width() - net_policy.width.desired as i32).max(0);
         let hshrink = match node.halign() {
-            HorizontalAlignment::Left => {
-                PixelSideOffsets::new(0, slack, 0, 0)
-            },
+            HorizontalAlignment::Left => PixelSideOffsets::new(0, slack, 0, 0),
             HorizontalAlignment::Centre => {
-                PixelSideOffsets::new(0, slack/2, 0, slack - (slack/2))
-            },
-            HorizontalAlignment::Right => {
-                PixelSideOffsets::new(0, 0, 0, slack)
-            },
+                PixelSideOffsets::new(0, slack / 2, 0, slack - (slack / 2))
+            }
+            HorizontalAlignment::Right => PixelSideOffsets::new(0, 0, 0, slack),
         };
         inside = inside.inner_box(hshrink);
     }
-    if net_policy.1.slack_weight == 0 {
-        let slack = (inside.height() - net_policy.1.desired as i32).max(0);
+    if net_policy.height.slack_weight == 0 {
+        let slack = (inside.height() - net_policy.height.desired as i32).max(0);
         let vshrink = match node.valign() {
-            VerticalAlignment::Top => {
-                PixelSideOffsets::new(0, 0, slack, 0)
-            },
+            VerticalAlignment::Top => PixelSideOffsets::new(0, 0, slack, 0),
             VerticalAlignment::Centre => {
-                PixelSideOffsets::new(slack/2, 0, slack-(slack/2), 0)
-            },
-            VerticalAlignment::Bottom => {
-                PixelSideOffsets::new(slack, 0, 0, 0)
-            },
+                PixelSideOffsets::new(slack / 2, 0, slack - (slack / 2), 0)
+            }
+            VerticalAlignment::Bottom => PixelSideOffsets::new(slack, 0, 0, 0),
         };
         inside = inside.inner_box(vshrink);
-        
     }
     println!("         | inside after:  {inside:?}");
     inside

+ 39 - 0
src/layout/arr/container.rs

@@ -0,0 +1,39 @@
+use super::ArrangementCalculator;
+use crate::{
+    layout::{LayoutNodeAccess, SizePolicy2D},
+    math::PixelBox,
+};
+
+#[derive(Clone, Debug)]
+pub struct ContainerArrangement;
+
+impl ArrangementCalculator for ContainerArrangement {
+    fn arrange_step(&self, node: LayoutNodeAccess) -> SizePolicy2D {
+        let Some(child) = node.child(0) else {
+            return (node.width_policy(), node.height_policy()).into();
+        };
+        SizePolicy2D::new(
+            node.width_policy.max_preserve_slack(child.width_policy()),
+            node.height_policy.max_preserve_slack(child.height_policy()),
+        ) + node.margin_as_policy()
+    }
+
+    fn layout_step(&self, node: LayoutNodeAccess, inside: PixelBox) {
+        let inside = super::do_align(&node, inside);
+
+        node.with_state(|ns| {
+            if Some(inside) != ns.area {
+                ns.area = Some(inside);
+                ns.needs_render = true;
+            }
+        });
+
+        let Some(child) = node.child(0) else {
+            return;
+        };
+
+        child
+            .child_arrangement
+            .layout_step(child, inside.inner_box(node.margin()));
+    }
+}

+ 16 - 22
src/layout/arr/line.rs

@@ -1,9 +1,6 @@
 use super::{do_fit, ArrangementCalculator};
 use crate::{
-    layout::{
-        cache::{LayoutCacheKey, NodeState},
-        LayoutNodeAccess, SizePolicy,
-    },
+    layout::{cache::NodeState, LayoutNodeAccess, SizePolicy, SizePolicy2D},
     math::{PixelBox, PixelPoint, PixelSize},
 };
 use std::ops::Add;
@@ -24,23 +21,19 @@ impl LineArrangement {
 }
 
 impl ArrangementCalculator for LineArrangement {
-    fn arrange_step(&self, node: LayoutNodeAccess) -> (SizePolicy, SizePolicy) {
+    fn arrange_step(&self, node: LayoutNodeAccess) -> SizePolicy2D {
         if node.child_len() == 0 {
-            return (node.width_policy, node.height_policy);
+            return (node.width_policy, node.height_policy).into();
         }
 
         let child_policies = node
             .child_iter()
-            .map(|child| {
-                node.cache
-                    .with_state(child.cache_key, |s| s.net_policy)
-                    .unwrap()
-            })
+            .map(|child| child.with_state(|s| s.net_policy).unwrap())
             .collect::<Vec<_>>();
 
         let cw_policy = child_policies
             .iter()
-            .map(|v| v.0)
+            .map(|v| v.width)
             .reduce(if self.is_column() {
                 SizePolicy::max
             } else {
@@ -49,23 +42,23 @@ impl ArrangementCalculator for LineArrangement {
             .unwrap();
         let ch_policy: SizePolicy = child_policies
             .iter()
-            .map(|v| v.1)
+            .map(|v| v.height)
             .reduce(if self.is_column() {
                 SizePolicy::add
             } else {
                 SizePolicy::max
             })
             .unwrap();
-        (
+        SizePolicy2D::new(
             node.width_policy.max_preserve_slack(cw_policy),
-            node.height_policy.max_preserve_slack(ch_policy),
-        )
+            node.height_policy.max_preserve_slack(ch_policy)
+        ) + node.margin_as_policy()
     }
 
     fn layout_step(&self, node: LayoutNodeAccess, inside: PixelBox) {
         let inside = super::do_align(&node, inside);
         // do the final children layouts
-        node.cache.with_state(node.cache_key, |ns| {
+        node.with_state(|ns| {
             if Some(inside) != ns.area {
                 ns.area = Some(inside);
                 ns.needs_render = true;
@@ -76,16 +69,17 @@ impl ArrangementCalculator for LineArrangement {
             return;
         }
 
+        // add margins
+        let inside = inside.inner_box(node.margin);
+
         // expansion direction extraction lambda
         let ee = if self.is_column() {
-            |ns: &mut NodeState| ns.net_policy.1
+            |ns: &mut NodeState| ns.net_policy.height
         } else {
-            |ns: &mut NodeState| ns.net_policy.0
+            |ns: &mut NodeState| ns.net_policy.width
         };
 
-        let policies = node
-            .child_iter()
-            .map(|c| node.cache.with_state(c.cache_key, ee).unwrap());
+        let policies = node.child_iter().map(|c| c.with_state(ee).unwrap());
         let mut last_offset = 0;
         let fit = do_fit(
             if self.is_column() {

+ 11 - 15
src/layout/arr/table.rs

@@ -2,7 +2,7 @@ use super::{do_fit, ArrangementCalculator};
 use crate::{
     layout::{
         cache::{LayoutCacheKey, NodeState},
-        LayoutNodeAccess, SizePolicy, TableCell, TableSize,
+        LayoutNodeAccess, SizePolicy, TableCell, TableSize, SizePolicy2D,
     },
     math::{PixelBox, PixelPoint, PixelSize},
 };
@@ -48,12 +48,9 @@ impl TableArrangement {
                 tstate.row_policies.resize(cell.y + 1, SizePolicy::zero());
             }
 
-            let child_policy = node
-                .cache
-                .with_state(ch.cache_key, |v| v.net_policy)
-                .unwrap();
-            tstate.col_policies[cell.x] = tstate.col_policies[cell.x].max(child_policy.0);
-            tstate.row_policies[cell.y] = tstate.row_policies[cell.y].max(child_policy.1);
+            let child_policy = ch.with_state(|v| v.net_policy).unwrap();
+            tstate.col_policies[cell.x] = tstate.col_policies[cell.x].max(child_policy.width);
+            tstate.row_policies[cell.y] = tstate.row_policies[cell.y].max(child_policy.height);
         }
 
         tstate
@@ -61,12 +58,12 @@ impl TableArrangement {
 }
 
 impl ArrangementCalculator for TableArrangement {
-    fn arrange_step(&self, node: LayoutNodeAccess) -> (SizePolicy, SizePolicy) {
+    fn arrange_step(&self, node: LayoutNodeAccess) -> SizePolicy2D {
         let tstate = self.build_table_state(&node);
 
         // if there are no table cells...
         if tstate.row_policies.len() == 0 || tstate.col_policies.len() == 0 {
-            return (node.width_policy(), node.height_policy());
+            return (node.width_policy(), node.height_policy()).into();
         }
 
         let net_rows = tstate
@@ -80,22 +77,24 @@ impl ArrangementCalculator for TableArrangement {
             .reduce(SizePolicy::add)
             .unwrap();
 
-        (
+        SizePolicy2D::new(
             node.width_policy.max_preserve_slack(net_cols),
             node.height_policy.max_preserve_slack(net_rows),
-        )
+        ) + node.margin_as_policy()
     }
 
     fn layout_step(&self, node: LayoutNodeAccess, inside: PixelBox) {
         let inside = super::do_align(&node, inside);
 
-        node.cache.with_state(node.cache_key, |ns| {
+        node.with_state(|ns| {
             if Some(inside) != ns.area {
                 ns.area = Some(inside);
                 ns.needs_render = true;
             }
         });
 
+        let inside = inside.inner_box(node.margin());
+
         let tstate = self.build_table_state(&node);
 
         let mut col_offsets = vec![0];
@@ -103,9 +102,6 @@ impl ArrangementCalculator for TableArrangement {
         let mut row_offsets = vec![0];
         row_offsets.extend(do_fit(inside.height(), tstate.row_policies.into_iter()));
 
-        // XXX: do 'alignment' of child stack here via an offset?
-        // let x_off = 
-
         for ch in node.child_iter() {
             let Some(cell) = ch.table_cell() else {
                 continue;

+ 2 - 2
src/layout/cache.rs

@@ -1,6 +1,6 @@
 use std::{cell::RefCell, collections::HashMap, rc::Rc};
 
-use super::{SizePolicy, TableCell};
+use super::{SizePolicy, TableCell, SizePolicy2D};
 
 #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
 pub struct LayoutCacheKey(usize);
@@ -15,7 +15,7 @@ impl LayoutCacheKey {
 pub struct NodeState {
     pub(super) needs_update: bool,
     pub(super) needs_render: bool,
-    pub(super) net_policy: (SizePolicy, SizePolicy),
+    pub(super) net_policy: SizePolicy2D,
     pub(super) layer: Layer,
     pub(super) area: Option<kahlo::math::PixelBox>,
     pub(super) children: Vec<LayoutCacheKey>,

+ 9 - 15
src/layout/calc.rs

@@ -19,7 +19,7 @@ pub fn recalculate(node: LayoutNodeAccess) {
 
 fn arrangement_pass(parent: Option<LayoutCacheKey>, node: LayoutNodeAccess, layer: Layer) {
     // early-out check
-    if node.cache.with_state(node.cache_key, |st| st.needs_update) == Some(false) {
+    if node.with_state(|st| st.needs_update) == Some(false) {
         return;
     }
 
@@ -28,12 +28,12 @@ fn arrangement_pass(parent: Option<LayoutCacheKey>, node: LayoutNodeAccess, laye
     }
 
     // construct net size policy
-    let (wpol, hpol) = node.child_arrangement.arrange_step(node);
+    let net_policy = node.child_arrangement.arrange_step(node);
 
     let child_ids = node.child_iter().map(|ch| ch.cache_key).collect();
     if node.cache.has_state_for(node.cache_key) {
-        node.cache.with_state(node.cache_key, |ns| {
-            ns.net_policy = (wpol, hpol);
+        node.with_state(move |ns| {
+            ns.net_policy = net_policy;
             ns.children = child_ids;
         });
     } else {
@@ -43,7 +43,7 @@ fn arrangement_pass(parent: Option<LayoutCacheKey>, node: LayoutNodeAccess, laye
             NodeState {
                 needs_update: false,
                 needs_render: true,
-                net_policy: (wpol, hpol),
+                net_policy,
                 area: None,
                 layer,
                 children: child_ids,
@@ -55,7 +55,7 @@ fn arrangement_pass(parent: Option<LayoutCacheKey>, node: LayoutNodeAccess, laye
 
 #[cfg(test)]
 mod test {
-    use kahlo::math::{PixelBox, PixelPoint, PixelRect, PixelSize};
+    use kahlo::math::{PixelBox, PixelPoint, PixelSize};
 
     use crate::layout::{
         cache::LayoutCache, dump_node_tree, ChildArrangement, LayoutNode, LayoutNodeAccess,
@@ -125,9 +125,7 @@ mod test {
 
         // check that final rects match expectations
         assert_eq!(
-            cache
-                .with_state(root.node.cache_key, |ns| ns.area)
-                .flatten(),
+            root.node.with_state(|ns| ns.area).flatten(),
             Some(PixelBox::from_origin_and_size(
                 PixelPoint::new(1, 1),
                 PixelSize::new(2, 5)
@@ -135,9 +133,7 @@ mod test {
         );
 
         assert_eq!(
-            cache
-                .with_state(root.children[0].node.cache_key, |ns| ns.area)
-                .flatten(),
+            root.children[0].node.with_state(|ns| ns.area).flatten(),
             Some(PixelBox::from_origin_and_size(
                 PixelPoint::new(1, 1),
                 PixelSize::new(2, 2)
@@ -349,9 +345,7 @@ mod test {
         println!("after recalculate:\n{dump}");
 
         assert_eq!(
-            cache
-                .with_state(root.children[0].node.cache_key, |ns| ns.area)
-                .flatten(),
+            root.children[0].node.with_state(|ns| ns.area).flatten(),
             Some(PixelBox::from_origin_and_size(
                 PixelPoint::new(0, 0),
                 PixelSize::new(8, 8)

+ 21 - 3
src/widget.rs

@@ -1,13 +1,14 @@
 use crate::{
     component::Component,
     input::InputState,
-    layout::{LayoutNode, LayoutNodeAccess, TableCell, HorizontalAlignment, VerticalAlignment},
+    layout::{HorizontalAlignment, LayoutNode, LayoutNodeAccess, TableCell, VerticalAlignment},
     theme::Theme,
     ui::UIHandle,
     window::RenderFormat,
 };
 
 mod label;
+use kahlo::math::PixelSideOffsets;
 pub use label::Label;
 mod frame;
 pub use frame::Frame;
@@ -41,10 +42,24 @@ pub trait WidgetExt<C: Component>: Widget<C> {
         Frame::wrap_widget(self)
     }
 
+    fn set_margins(&mut self, margins: PixelSideOffsets) {
+        *self.layout_node_mut().margin_mut() = margins;
+    }
+    fn with_margins(mut self, margins: PixelSideOffsets) -> Self
+    where
+        Self: Sized,
+    {
+        self.set_margins(margins);
+        self
+    }
+
     fn set_halign(&mut self, halign: HorizontalAlignment) {
         self.layout_node_mut().set_halign(halign);
     }
-    fn with_halign(mut self, halign: HorizontalAlignment) -> Self where Self: Sized {
+    fn with_halign(mut self, halign: HorizontalAlignment) -> Self
+    where
+        Self: Sized,
+    {
         self.set_halign(halign);
         self
     }
@@ -52,7 +67,10 @@ pub trait WidgetExt<C: Component>: Widget<C> {
     fn set_valign(&mut self, valign: VerticalAlignment) {
         self.layout_node_mut().set_valign(valign);
     }
-    fn with_valign(mut self, valign: VerticalAlignment) -> Self where Self: Sized {
+    fn with_valign(mut self, valign: VerticalAlignment) -> Self
+    where
+        Self: Sized,
+    {
         self.set_valign(valign);
         self
     }

+ 3 - 1
src/widget/frame.rs

@@ -2,7 +2,7 @@ use kahlo::{math::PixelSideOffsets, prelude::*};
 
 use crate::{
     component::Component,
-    layout::{LayoutNode, LayoutNodeAccess, LayoutNodeContainer, SizePolicy},
+    layout::{ChildArrangement, LayoutNode, LayoutNodeAccess, LayoutNodeContainer, SizePolicy},
     theme::Theme,
     ui::UIHandle,
     window::RenderFormat,
@@ -19,6 +19,7 @@ impl<C: Component> Frame<C> {
     pub fn new(uih: &UIHandle) -> Self {
         let mut nnode = uih.new_layout_node();
         nnode
+            .set_arrangement(ChildArrangement::Container)
             .set_height_policy(SizePolicy::expanding(1))
             .set_width_policy(SizePolicy::expanding(1));
         Self {
@@ -36,6 +37,7 @@ impl<C: Component> Frame<C> {
     pub fn wrap_widget<W: 'static + Widget<C>>(child: W) -> Self {
         let mut nnode = LayoutNode::new(child.layout_node().cache().clone());
         nnode
+            .set_arrangement(ChildArrangement::Container)
             .set_height_policy(SizePolicy::expanding(1))
             .set_width_policy(SizePolicy::expanding(1));
         Self {

+ 1 - 4
src/widget/label.rs

@@ -1,9 +1,6 @@
 use std::cell::RefCell;
 
-use kahlo::{
-    math::PixelBox,
-    prelude::*,
-};
+use kahlo::{math::PixelBox, prelude::*};
 
 use crate::{
     component::Component,