1use super::{Item, ItemConsts, ItemRc, RenderingResult};
11use crate::component_factory::{ComponentFactory, FactoryContext};
12use crate::input::{
13 FocusEvent, FocusEventResult, InputEventFilterResult, InputEventResult, KeyEvent,
14 KeyEventResult, MouseEvent,
15};
16use crate::item_rendering::{CachedRenderingData, RenderRectangle};
17use crate::item_tree::{IndexRange, ItemTreeRc, ItemTreeWeak, ItemWeak};
18use crate::item_tree::{ItemTreeNode, ItemVisitorVTable, TraversalOrder, VisitChildrenResult};
19use crate::layout::{LayoutInfo, Orientation};
20use crate::lengths::{LogicalLength, LogicalRect, LogicalSize};
21use crate::properties::{Property, PropertyTracker};
22#[cfg(feature = "rtti")]
23use crate::rtti::*;
24use crate::window::WindowAdapter;
25use alloc::boxed::Box;
26use alloc::rc::Rc;
27use const_field_offset::FieldOffsets;
28use core::cell::RefCell;
29use core::pin::Pin;
30use i_slint_core_macros::*;
31use once_cell::unsync::OnceCell;
32
33#[repr(C)]
34#[derive(FieldOffsets, Default, SlintElement)]
35#[pin]
36pub struct ComponentContainer {
38 pub width: Property<LogicalLength>,
39 pub height: Property<LogicalLength>,
40 pub component_factory: Property<ComponentFactory>,
41 pub has_component: Property<bool>,
42
43 pub cached_rendering_data: CachedRenderingData,
44
45 component_tracker: OnceCell<Pin<Box<PropertyTracker>>>,
46 item_tree: RefCell<Option<ItemTreeRc>>,
47
48 my_component: OnceCell<ItemTreeWeak>,
49 embedding_item_tree_index: OnceCell<u32>,
50 self_weak: OnceCell<ItemWeak>,
51}
52
53impl ComponentContainer {
54 pub fn ensure_updated(self: Pin<&Self>) {
55 let factory = self
56 .component_tracker
57 .get()
58 .unwrap()
59 .as_ref()
60 .evaluate_if_dirty(|| self.component_factory());
61
62 let Some(factory) = factory else {
63 return;
64 };
65
66 let mut window = None;
67 if let Some(parent) = self.my_component.get().and_then(|x| x.upgrade()) {
68 vtable::VRc::borrow_pin(&parent).as_ref().window_adapter(false, &mut window);
69 }
70 let prevent_focus_change =
71 window.as_ref().is_some_and(|w| w.window().0.prevent_focus_change.replace(true));
72
73 let factory_context = FactoryContext {
74 parent_item_tree: self.my_component.get().unwrap().clone(),
75 parent_item_tree_index: *self.embedding_item_tree_index.get().unwrap(),
76 };
77
78 let product = factory.build(factory_context);
79
80 if let Some(w) = window {
81 w.window().0.prevent_focus_change.set(prevent_focus_change);
82 }
83
84 if let Some(item_tree) = product.clone() {
85 let item_tree = vtable::VRc::borrow_pin(&item_tree);
86 let root_item = item_tree.as_ref().get_item_ref(0);
87 if let Some(window_item) =
88 crate::items::ItemRef::downcast_pin::<crate::items::WindowItem>(root_item)
89 {
90 let weak = self.self_weak.get().unwrap().clone();
93 window_item.width.set_binding(Box::new(move || {
94 if let Some(self_rc) = weak.upgrade() {
95 let self_pin = self_rc.borrow();
96 if let Some(self_cc) = crate::items::ItemRef::downcast_pin::<Self>(self_pin)
97 {
98 return self_cc.width();
99 }
100 }
101 Default::default()
102 }));
103 let weak = self.self_weak.get().unwrap().clone();
104 window_item.height.set_binding(Box::new(move || {
105 if let Some(self_rc) = weak.upgrade() {
106 let self_pin = self_rc.borrow();
107 if let Some(self_cc) = crate::items::ItemRef::downcast_pin::<Self>(self_pin)
108 {
109 return self_cc.height();
110 }
111 }
112 Default::default()
113 }));
114 }
115 }
116
117 self.has_component.set(product.is_some());
118
119 self.item_tree.replace(product);
120 }
121
122 pub fn subtree_range(self: Pin<&Self>) -> IndexRange {
123 IndexRange { start: 0, end: if self.item_tree.borrow().is_some() { 1 } else { 0 } }
124 }
125
126 pub fn subtree_component(self: Pin<&Self>) -> ItemTreeWeak {
127 self.item_tree.borrow().as_ref().map_or(ItemTreeWeak::default(), vtable::VRc::downgrade)
128 }
129
130 pub fn visit_children_item(
131 self: Pin<&Self>,
132 _index: isize,
133 order: TraversalOrder,
134 visitor: vtable::VRefMut<ItemVisitorVTable>,
135 ) -> VisitChildrenResult {
136 let rc = self.item_tree.borrow().clone();
137 if let Some(rc) = &rc {
138 vtable::VRc::borrow_pin(rc).as_ref().visit_children_item(-1, order, visitor)
139 } else {
140 VisitChildrenResult::CONTINUE
141 }
142 }
143}
144
145impl Item for ComponentContainer {
146 fn init(self: Pin<&Self>, self_rc: &ItemRc) {
147 let rc = self_rc.item_tree();
148
149 self.my_component.set(vtable::VRc::downgrade(rc)).ok().unwrap();
150
151 let pin_rc = vtable::VRc::borrow_pin(rc);
153 let item_tree = pin_rc.as_ref().get_item_tree();
154 let ItemTreeNode::Item { children_index, children_count, .. } =
155 item_tree[self_rc.index() as usize]
156 else {
157 panic!("ComponentContainer not found in item tree");
158 };
159
160 assert_eq!(children_count, 1);
161 assert!(matches!(item_tree[children_index as usize], ItemTreeNode::DynamicTree { .. }));
162
163 self.embedding_item_tree_index.set(children_index).ok().unwrap();
164
165 self.component_tracker.set(Box::pin(PropertyTracker::default())).ok().unwrap();
166 self.self_weak.set(self_rc.downgrade()).ok().unwrap();
167 }
168
169 fn layout_info(
170 self: Pin<&Self>,
171 orientation: Orientation,
172 _window_adapter: &Rc<dyn WindowAdapter>,
173 _self_rc: &ItemRc,
174 ) -> LayoutInfo {
175 self.ensure_updated();
176 if let Some(rc) = self.item_tree.borrow().clone() {
177 vtable::VRc::borrow_pin(&rc).as_ref().layout_info(orientation)
178 } else {
179 Default::default()
180 }
181 }
182
183 fn input_event_filter_before_children(
184 self: Pin<&Self>,
185 _: &MouseEvent,
186 _window_adapter: &Rc<dyn WindowAdapter>,
187 _self_rc: &ItemRc,
188 ) -> InputEventFilterResult {
189 InputEventFilterResult::ForwardAndIgnore
190 }
191
192 fn input_event(
193 self: Pin<&Self>,
194 _: &MouseEvent,
195 _window_adapter: &Rc<dyn WindowAdapter>,
196 _self_rc: &ItemRc,
197 ) -> InputEventResult {
198 InputEventResult::EventIgnored
199 }
200
201 fn focus_event(
202 self: Pin<&Self>,
203 _: &FocusEvent,
204 _window_adapter: &Rc<dyn WindowAdapter>,
205 _self_rc: &ItemRc,
206 ) -> FocusEventResult {
207 FocusEventResult::FocusIgnored
208 }
209
210 fn capture_key_event(
211 self: Pin<&Self>,
212 _: &KeyEvent,
213 _window_adapter: &Rc<dyn WindowAdapter>,
214 _self_rc: &ItemRc,
215 ) -> KeyEventResult {
216 KeyEventResult::EventIgnored
217 }
218
219 fn key_event(
220 self: Pin<&Self>,
221 _: &KeyEvent,
222 _window_adapter: &Rc<dyn WindowAdapter>,
223 _self_rc: &ItemRc,
224 ) -> KeyEventResult {
225 KeyEventResult::EventIgnored
226 }
227
228 fn render(
229 self: Pin<&Self>,
230 backend: &mut super::ItemRendererRef,
231 item_rc: &ItemRc,
232 size: LogicalSize,
233 ) -> RenderingResult {
234 backend.draw_rectangle(self, item_rc, size, &self.cached_rendering_data);
235 RenderingResult::ContinueRenderingChildren
236 }
237
238 fn bounding_rect(
239 self: core::pin::Pin<&Self>,
240 _window_adapter: &Rc<dyn WindowAdapter>,
241 _self_rc: &ItemRc,
242 geometry: LogicalRect,
243 ) -> LogicalRect {
244 geometry
245 }
246
247 fn clips_children(self: core::pin::Pin<&Self>) -> bool {
248 false
249 }
250}
251
252impl RenderRectangle for ComponentContainer {
253 fn background(self: Pin<&Self>) -> crate::Brush {
254 self.item_tree
255 .borrow()
256 .clone()
257 .and_then(|item_tree| {
258 let item_tree = vtable::VRc::borrow_pin(&item_tree);
259 let root_item = item_tree.as_ref().get_item_ref(0);
260 crate::items::ItemRef::downcast_pin::<crate::items::WindowItem>(root_item)
261 .map(|window_item| window_item.background())
262 })
263 .unwrap_or_default()
264 }
265}
266
267impl ItemConsts for ComponentContainer {
268 const cached_rendering_data_offset: const_field_offset::FieldOffset<
269 ComponentContainer,
270 CachedRenderingData,
271 > = ComponentContainer::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
272}