1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4/*!
5This module contains the builtin `ComponentContainer` and related items
6
7When adding an item or a property, it needs to be kept in sync with different place.
8Lookup the [`crate::items`] module documentation.
9*/
10use 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]
36/// The implementation of the `ComponentContainer` element
37pub 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 // Do _not_ use a two-way binding: That causes evaluations of width and height to
91 // assert on recursive property evaluation.
92 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 // Find my embedding item_tree_index:
152 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: child_item_tree_index, .. } =
155 item_tree[self_rc.index() as usize]
156 else {
157 panic!("Internal compiler error: ComponentContainer had no child.");
158 };
159
160 self.embedding_item_tree_index.set(child_item_tree_index).ok().unwrap();
161
162 self.component_tracker.set(Box::pin(PropertyTracker::default())).ok().unwrap();
163 self.self_weak.set(self_rc.downgrade()).ok().unwrap();
164 }
165
166 fn layout_info(
167 self: Pin<&Self>,
168 orientation: Orientation,
169 _window_adapter: &Rc<dyn WindowAdapter>,
170 ) -> LayoutInfo {
171 self.ensure_updated();
172 if let Some(rc) = self.item_tree.borrow().clone() {
173 vtable::VRc::borrow_pin(&rc).as_ref().layout_info(orientation)
174 } else {
175 Default::default()
176 }
177 }
178
179 fn input_event_filter_before_children(
180 self: Pin<&Self>,
181 _: MouseEvent,
182 _window_adapter: &Rc<dyn WindowAdapter>,
183 _self_rc: &ItemRc,
184 ) -> InputEventFilterResult {
185 InputEventFilterResult::ForwardAndIgnore
186 }
187
188 fn input_event(
189 self: Pin<&Self>,
190 _: MouseEvent,
191 _window_adapter: &Rc<dyn WindowAdapter>,
192 _self_rc: &ItemRc,
193 ) -> InputEventResult {
194 InputEventResult::EventIgnored
195 }
196
197 fn focus_event(
198 self: Pin<&Self>,
199 _: &FocusEvent,
200 _window_adapter: &Rc<dyn WindowAdapter>,
201 _self_rc: &ItemRc,
202 ) -> FocusEventResult {
203 FocusEventResult::FocusIgnored
204 }
205
206 fn key_event(
207 self: Pin<&Self>,
208 _: &KeyEvent,
209 _window_adapter: &Rc<dyn WindowAdapter>,
210 _self_rc: &ItemRc,
211 ) -> KeyEventResult {
212 KeyEventResult::EventIgnored
213 }
214
215 fn render(
216 self: Pin<&Self>,
217 backend: &mut super::ItemRendererRef,
218 item_rc: &ItemRc,
219 size: LogicalSize,
220 ) -> RenderingResult {
221 backend.draw_rectangle(self, item_rc, size, &self.cached_rendering_data);
222 RenderingResult::ContinueRenderingChildren
223 }
224
225 fn bounding_rect(
226 self: core::pin::Pin<&Self>,
227 _window_adapter: &Rc<dyn WindowAdapter>,
228 _self_rc: &ItemRc,
229 geometry: LogicalRect,
230 ) -> LogicalRect {
231 geometry
232 }
233
234 fn clips_children(self: core::pin::Pin<&Self>) -> bool {
235 false
236 }
237}
238
239impl RenderRectangle for ComponentContainer {
240 fn background(self: Pin<&Self>) -> crate::Brush {
241 self.item_tree
242 .borrow()
243 .clone()
244 .and_then(|item_tree: VRc<{unknown}, {unknown}>| {
245 let item_tree: Pin> = vtable::VRc::borrow_pin(&item_tree);
246 let root_item = item_tree.as_ref().get_item_ref(0);
247 crate::items::ItemRef::downcast_pin::<crate::items::WindowItem>(root_item)
248 .map(|window_item| window_item.background())
249 })
250 .unwrap_or_default()
251 }
252}
253
254impl ItemConsts for ComponentContainer {
255 const cached_rendering_data_offset: const_field_offset::FieldOffset<
256 ComponentContainer,
257 CachedRenderingData,
258 > = ComponentContainer::FIELD_OFFSETS.cached_rendering_data.as_unpinned_projection();
259}
260