telar_ui_core/
layout_item.rs1use std::cell::RefCell;
2use std::rc::Rc;
3
4use geometry_core::Rect;
5use layout_core::{LayoutError, LayoutStyle, NodeId};
6use platform_core::Event;
7use reactive_core::RwSignal;
8use ui_tree::{Component, EventResult, RenderNode, Segment};
9
10use crate::context::{new_container, track_layout};
11use crate::layout_leaf::LayoutLeaf;
12
13#[derive(Clone)]
19pub(crate) struct Child {
20 pub(crate) item: Rc<RefCell<Box<dyn LayoutItem>>>,
21 pub(crate) rect: Option<RwSignal<Rect>>,
22 pub(crate) segment: Rc<Segment>,
23}
24
25impl Child {
26 pub(crate) fn node(&self) -> layout_core::NodeId {
28 self.item.borrow().layout_node()
29 }
30}
31
32pub(crate) fn make_child(widget: Box<dyn LayoutItem>) -> Child {
36 let rect = track_layout(widget.layout_node());
37 let item = Rc::new(RefCell::new(widget));
38 let segment = mount_item_segment(Rc::clone(&item));
39 Child {
40 item,
41 rect,
42 segment,
43 }
44}
45
46pub(crate) type TrackedChildren = Vec<Child>;
47
48pub(crate) fn mount_item_segment(item: Rc<RefCell<Box<dyn LayoutItem>>>) -> Rc<Segment> {
52 let name = item
53 .try_borrow()
54 .map(|i| i.debug_name())
55 .unwrap_or("Component");
56 Segment::mount_fn_named(name, move || item.try_borrow().ok().map(|i| i.view()))
57}
58
59pub(crate) trait LeafWidget {
60 fn layout_leaf(&self) -> &LayoutLeaf;
61}
62
63pub trait LayoutItem: Component {
64 fn layout_node(&self) -> NodeId;
65}
66
67pub struct ClippedItem {
72 inner: Box<dyn LayoutItem>,
73 rect: RwSignal<Rect>,
74}
75
76impl ClippedItem {
77 pub fn new(inner: Box<dyn LayoutItem>) -> Self {
78 let rect = track_layout(inner.layout_node()).expect("clipped item's node not registered");
79 Self { inner, rect }
80 }
81}
82
83impl LayoutItem for ClippedItem {
84 fn layout_node(&self) -> NodeId {
85 self.inner.layout_node()
86 }
87}
88
89impl Component for ClippedItem {
90 fn view(&self) -> RenderNode {
91 RenderNode::Clip {
92 rect: self.rect.get(),
93 radius: renderer_core::BorderRadius::zero(),
94 children: ui_tree::NodeVec::collect([self.inner.view()]),
95 }
96 }
97
98 fn on_event(&mut self, event: &Event) -> EventResult {
99 self.inner.on_event(event)
100 }
101
102 fn debug_name(&self) -> &'static str {
103 "Clipped"
104 }
105}
106
107impl<T: LeafWidget + Component> LayoutItem for T {
108 fn layout_node(&self) -> NodeId {
109 self.layout_leaf().node
110 }
111}
112
113impl Component for Box<dyn LayoutItem> {
115 fn view(&self) -> RenderNode {
116 (**self).view()
117 }
118
119 fn on_event(&mut self, event: &Event) -> EventResult {
120 (**self).on_event(event)
121 }
122
123 fn debug_name(&self) -> &'static str {
124 (**self).debug_name()
125 }
126}
127
128impl LayoutItem for Box<dyn LayoutItem> {
129 fn layout_node(&self) -> NodeId {
130 (**self).layout_node()
131 }
132}
133
134pub fn box_item(item: impl LayoutItem + 'static) -> Box<dyn LayoutItem> {
136 Box::new(item)
137}
138
139pub(crate) fn register_container(
140 layout_style: LayoutStyle,
141 children: Vec<Box<dyn LayoutItem>>,
142) -> Result<(NodeId, RwSignal<Rect>, TrackedChildren), LayoutError> {
143 let child_nodes = children.iter().map(|c| c.layout_node()).collect::<Vec<_>>();
144 let node = new_container(layout_style, &child_nodes)?;
145 let rect = track_layout(node).expect("new_container always registers a signal");
146 let children = children.into_iter().map(make_child).collect();
147 Ok((node, rect, children))
148}
149
150#[macro_export]
152macro_rules! impl_leaf_widget {
153 ($struct:ident) => {
154 impl $crate::layout_item::LeafWidget for $struct {
155 fn layout_leaf(&self) -> &$crate::layout_leaf::LayoutLeaf {
156 &self.leaf
157 }
158 }
159 };
160}