i_slint_compiler/passes/
flickable.rs1use crate::expression_tree::{BindingExpression, Expression, MinMaxOp, NamedReference};
14use crate::langtype::{ElementType, NativeClass, Type};
15use crate::layout::is_layout;
16use crate::object_tree::{Component, Element, ElementRc};
17use crate::typeregister::TypeRegister;
18use smol_str::{SmolStr, format_smolstr};
19use std::rc::Rc;
20use std::sync::Arc;
21
22pub fn is_flickable_element(element: &ElementRc) -> bool {
23 matches!(&element.borrow().base_type, ElementType::Builtin(n) if n.name == "Flickable")
24}
25
26pub fn handle_flickable(root_component: &Rc<Component>, tr: &TypeRegister) {
27 let mut native_empty = tr.empty_type().as_builtin().native_class.clone();
28 while let Some(p) = native_empty.parent.clone() {
29 native_empty = p;
30 }
31
32 crate::object_tree::recurse_elem_including_sub_components(
33 root_component,
34 &(),
35 &mut |elem: &ElementRc, _| {
36 if !is_flickable_element(elem) {
37 return;
38 }
39
40 fixup_geometry(elem);
41 create_content_element(elem, &native_empty);
42 },
43 )
44}
45
46fn create_content_element(flickable: &ElementRc, native_empty: &Arc<NativeClass>) {
47 let children = std::mem::take(&mut flickable.borrow_mut().children);
48 let is_listview = children
49 .iter()
50 .find_map(|c| c.borrow().repeated.as_ref().and_then(|r| r.is_listview.clone()));
51
52 if let Some(listview) = &is_listview {
53 for c in &children {
57 if c.borrow().repeated.is_none() {
58 continue;
60 }
61 let ElementType::Component(base) = c.borrow().base_type.clone() else { continue };
62 let inner_elem = &base.root_element;
63 let new_y = crate::layout::create_new_prop(
64 inner_elem,
65 SmolStr::new_static("actual-y"),
66 Type::LogicalLength,
67 );
68 new_y.mark_as_set();
69 inner_elem.borrow_mut().set_binding(
70 "y".into(),
71 Expression::BinaryExpression {
72 lhs: Expression::PropertyReference(new_y.clone()).into(),
73 rhs: Expression::PropertyReference(listview.content_y.clone()).into(),
74 op: '-',
75 source_location: None,
76 }
77 .into(),
78 );
79 inner_elem.borrow_mut().geometry_props.as_mut().unwrap().y = new_y;
80 }
81 }
82
83 let content = Element::make_rc(Element {
84 id: format_smolstr!("{}-content", flickable.borrow().id),
85 base_type: ElementType::Native(native_empty.clone()),
86 children,
87 enclosing_component: flickable.borrow().enclosing_component.clone(),
88 is_flickable_content: true,
89 ..Element::default()
90 });
91 let element_type = flickable.borrow().base_type.clone();
92 for prop in element_type.as_builtin().properties.keys() {
93 if let Some(content_prop) = prop.strip_prefix("content-") {
95 if is_listview.is_some() && matches!(content_prop, "y" | "height") {
96 continue;
98 }
99 content.borrow_mut().set_binding(
100 content_prop.into(),
101 BindingExpression::new_two_way(NamedReference::new(flickable, prop.clone()).into()),
102 );
103 }
104 }
105 content
106 .borrow()
107 .property_analysis
108 .borrow_mut()
109 .entry("y".into())
110 .or_default()
111 .is_set_externally = true;
112 content
113 .borrow()
114 .property_analysis
115 .borrow_mut()
116 .entry("x".into())
117 .or_default()
118 .is_set_externally = true;
119
120 let enclosing_component = flickable.borrow().enclosing_component.upgrade().unwrap();
121 for insertion_point in enclosing_component.child_insertion_points.borrow_mut().values_mut() {
122 if std::rc::Rc::ptr_eq(&insertion_point.parent, flickable) {
123 insertion_point.parent = content.clone()
124 }
125 }
126
127 flickable.borrow_mut().children.push(content);
128}
129
130fn fixup_geometry(flickable_elem: &ElementRc) {
131 let forward_minmax_of = |prop: &'static str, op: MinMaxOp| {
132 set_binding_if_not_explicit(flickable_elem, prop, || {
133 flickable_elem
134 .borrow()
135 .children
136 .iter()
137 .filter(|x| is_layout(&x.borrow().base_type))
138 .filter(|x| x.borrow().repeated.is_none())
140 .map(|x| {
141 Expression::PropertyReference(NamedReference::new(x, SmolStr::new_static(prop)))
142 })
143 .reduce(|lhs, rhs| crate::builtin_macros::min_max_expression(lhs, rhs, op))
144 })
145 };
146
147 if !flickable_elem.borrow().is_binding_set("height", false) {
148 forward_minmax_of("max-height", MinMaxOp::Min);
149 forward_minmax_of("preferred-height", MinMaxOp::Min);
150 }
151 if !flickable_elem.borrow().is_binding_set("width", false) {
152 forward_minmax_of("max-width", MinMaxOp::Min);
153 forward_minmax_of("preferred-width", MinMaxOp::Min);
154 }
155 set_binding_if_not_explicit(flickable_elem, "content-width", || {
156 Some(
157 flickable_elem
158 .borrow()
159 .children
160 .iter()
161 .filter(|x| is_layout(&x.borrow().base_type))
162 .filter(|x| x.borrow().repeated.is_none())
164 .map(|x| {
165 Expression::PropertyReference(NamedReference::new(
166 x,
167 SmolStr::new_static("min-width"),
168 ))
169 })
170 .fold(
171 Expression::PropertyReference(NamedReference::new(
172 flickable_elem,
173 SmolStr::new_static("width"),
174 )),
175 |lhs, rhs| crate::builtin_macros::min_max_expression(lhs, rhs, MinMaxOp::Max),
176 ),
177 )
178 });
179 set_binding_if_not_explicit(flickable_elem, "content-height", || {
180 Some(
181 flickable_elem
182 .borrow()
183 .children
184 .iter()
185 .filter(|x| is_layout(&x.borrow().base_type))
186 .filter(|x| x.borrow().repeated.is_none())
188 .map(|x| {
189 Expression::PropertyReference(NamedReference::new(
190 x,
191 SmolStr::new_static("min-height"),
192 ))
193 })
194 .fold(
195 Expression::PropertyReference(NamedReference::new(
196 flickable_elem,
197 SmolStr::new_static("height"),
198 )),
199 |lhs, rhs| crate::builtin_macros::min_max_expression(lhs, rhs, MinMaxOp::Max),
200 ),
201 )
202 });
203}
204
205fn set_binding_if_not_explicit(
208 elem: &ElementRc,
209 property: &str,
210 expression: impl FnOnce() -> Option<Expression>,
211) {
212 if !elem.borrow().is_binding_set(property, false)
219 && let Some(e) = expression()
220 {
221 elem.borrow_mut().set_binding_if_not_set(property.into(), || e);
222 }
223}