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