i_slint_core/items/
component_container.rs

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, 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}