1use crate::diagnostics::BuildDiagnostics;
12use crate::expression_tree::{BindingExpression, Expression, MinMaxOp, NamedReference, Unit};
13use crate::langtype::{ElementType, Type};
14use crate::object_tree::*;
15use smol_str::{format_smolstr, SmolStr};
16use std::cell::RefCell;
17
18pub async fn lower_tabwidget(
19 doc: &Document,
20 type_loader: &mut crate::typeloader::TypeLoader,
21 diag: &mut BuildDiagnostics,
22) {
23 let mut build_diags_to_ignore = BuildDiagnostics::default();
25 let tabwidget_impl = type_loader
26 .import_component("std-widgets.slint", "TabWidgetImpl", &mut build_diags_to_ignore)
27 .await
28 .expect("can't load TabWidgetImpl from std-widgets.slint");
29 let tab_impl = type_loader
30 .import_component("std-widgets.slint", "TabImpl", &mut build_diags_to_ignore)
31 .await
32 .expect("can't load TabImpl from std-widgets.slint");
33 let tabbar_impl = type_loader
34 .import_component("std-widgets.slint", "TabBarImpl", &mut build_diags_to_ignore)
35 .await
36 .expect("can't load TabBarImpl from std-widgets.slint");
37 let empty_type = type_loader.global_type_registry.borrow().empty_type();
38
39 doc.visit_all_used_components(|component| {
40 recurse_elem_including_sub_components_no_borrow(component, &(), &mut |elem, _| {
41 if matches!(&elem.borrow().builtin_type(), Some(b) if b.name == "TabWidget") {
42 process_tabwidget(
43 elem,
44 ElementType::Component(tabwidget_impl.clone()),
45 ElementType::Component(tab_impl.clone()),
46 ElementType::Component(tabbar_impl.clone()),
47 &empty_type,
48 diag,
49 );
50 }
51 })
52 });
53}
54
55fn process_tabwidget(
56 elem: &ElementRc,
57 tabwidget_impl: ElementType,
58 tab_impl: ElementType,
59 tabbar_impl: ElementType,
60 empty_type: &ElementType,
61 diag: &mut BuildDiagnostics,
62) {
63 if matches!(&elem.borrow_mut().base_type, ElementType::Builtin(_)) {
64 return;
66 }
67
68 elem.borrow_mut().base_type = tabwidget_impl;
69 let mut children = std::mem::take(&mut elem.borrow_mut().children);
70 let num_tabs = children.len();
71 let mut tabs = Vec::new();
72 for child in &mut children {
73 if child.borrow().repeated.is_some() {
74 diag.push_error(
75 "dynamic tabs ('if' or 'for') are currently not supported".into(),
76 &*child.borrow(),
77 );
78 continue;
79 }
80 if child.borrow().base_type.to_string() != "Tab" {
81 assert!(diag.has_errors());
82 continue;
83 }
84 let index = tabs.len();
85 child.borrow_mut().base_type = empty_type.clone();
86 child
87 .borrow_mut()
88 .property_declarations
89 .insert(SmolStr::new_static("title"), Type::String.into());
90 set_geometry_prop(elem, child, "x", diag);
91 set_geometry_prop(elem, child, "y", diag);
92 set_geometry_prop(elem, child, "width", diag);
93 set_geometry_prop(elem, child, "height", diag);
94 let condition = Expression::BinaryExpression {
95 lhs: Expression::PropertyReference(NamedReference::new(
96 elem,
97 SmolStr::new_static("current-index"),
98 ))
99 .into(),
100 rhs: Expression::NumberLiteral(index as _, Unit::None).into(),
101 op: '=',
102 };
103 let old = child
104 .borrow_mut()
105 .bindings
106 .insert(SmolStr::new_static("visible"), RefCell::new(condition.into()));
107 if let Some(old) = old {
108 diag.push_error(
109 "The property 'visible' cannot be set for Tabs inside a TabWidget".to_owned(),
110 &old.into_inner(),
111 );
112 }
113 let role = crate::typeregister::BUILTIN
114 .with(|e| e.enums.AccessibleRole.clone())
115 .try_value_from_string("tab-panel")
116 .unwrap();
117 let old = child.borrow_mut().bindings.insert(
118 SmolStr::new_static("accessible-role"),
119 RefCell::new(Expression::EnumerationValue(role).into()),
120 );
121 if let Some(old) = old {
122 diag.push_error(
123 "The property 'accessible-role' cannot be set for Tabs inside a TabWidget"
124 .to_owned(),
125 &old.into_inner(),
126 );
127 }
128 let title_ref = RefCell::new(
129 Expression::PropertyReference(NamedReference::new(child, "title".into())).into(),
130 );
131 let old = child.borrow_mut().bindings.insert("accessible-label".into(), title_ref);
132 if let Some(old) = old {
133 diag.push_error(
134 "The property 'accessible-label' cannot be set for Tabs inside a TabWidget"
135 .to_owned(),
136 &old.into_inner(),
137 );
138 }
139
140 let mut tab = Element {
141 id: format_smolstr!("{}-tab{}", elem.borrow().id, index),
142 base_type: tab_impl.clone(),
143 enclosing_component: elem.borrow().enclosing_component.clone(),
144 ..Default::default()
145 };
146 tab.bindings.insert(
147 SmolStr::new_static("title"),
148 BindingExpression::new_two_way(NamedReference::new(
149 child,
150 SmolStr::new_static("title"),
151 ))
152 .into(),
153 );
154 tab.bindings.insert(
155 SmolStr::new_static("current"),
156 BindingExpression::new_two_way(NamedReference::new(
157 elem,
158 SmolStr::new_static("current-index"),
159 ))
160 .into(),
161 );
162 tab.bindings.insert(
163 SmolStr::new_static("current-focused"),
164 BindingExpression::new_two_way(NamedReference::new(
165 elem,
166 SmolStr::new_static("current-focused"),
167 ))
168 .into(),
169 );
170 tab.bindings.insert(
171 SmolStr::new_static("tab-index"),
172 RefCell::new(Expression::NumberLiteral(index as _, Unit::None).into()),
173 );
174 tab.bindings.insert(
175 SmolStr::new_static("num-tabs"),
176 RefCell::new(Expression::NumberLiteral(num_tabs as _, Unit::None).into()),
177 );
178 tabs.push(Element::make_rc(tab));
179 }
180
181 let tabbar = Element {
182 id: format_smolstr!("{}-tabbar", elem.borrow().id),
183 base_type: tabbar_impl,
184 enclosing_component: elem.borrow().enclosing_component.clone(),
185 children: tabs,
186 ..Default::default()
187 };
188 let tabbar = Element::make_rc(tabbar);
189 set_tabbar_geometry_prop(elem, &tabbar, "x");
190 set_tabbar_geometry_prop(elem, &tabbar, "y");
191 set_tabbar_geometry_prop(elem, &tabbar, "width");
192 set_tabbar_geometry_prop(elem, &tabbar, "height");
193 tabbar.borrow_mut().bindings.insert(
194 SmolStr::new_static("num-tabs"),
195 RefCell::new(Expression::NumberLiteral(num_tabs as _, Unit::None).into()),
196 );
197 tabbar.borrow_mut().bindings.insert(
198 SmolStr::new_static("current"),
199 BindingExpression::new_two_way(NamedReference::new(
200 elem,
201 SmolStr::new_static("current-index"),
202 ))
203 .into(),
204 );
205 elem.borrow_mut().bindings.insert(
206 SmolStr::new_static("current-focused"),
207 BindingExpression::new_two_way(NamedReference::new(
208 &tabbar,
209 SmolStr::new_static("current-focused"),
210 ))
211 .into(),
212 );
213 elem.borrow_mut().bindings.insert(
214 SmolStr::new_static("tabbar-preferred-width"),
215 BindingExpression::new_two_way(NamedReference::new(
216 &tabbar,
217 SmolStr::new_static("preferred-width"),
218 ))
219 .into(),
220 );
221 elem.borrow_mut().bindings.insert(
222 SmolStr::new_static("tabbar-preferred-height"),
223 BindingExpression::new_two_way(NamedReference::new(
224 &tabbar,
225 SmolStr::new_static("preferred-height"),
226 ))
227 .into(),
228 );
229
230 if let Some(expr) = children
231 .iter()
232 .map(|x| {
233 Expression::PropertyReference(NamedReference::new(x, SmolStr::new_static("min-width")))
234 })
235 .reduce(|lhs, rhs| crate::builtin_macros::min_max_expression(lhs, rhs, MinMaxOp::Max))
236 {
237 elem.borrow_mut().bindings.insert("content-min-width".into(), RefCell::new(expr.into()));
238 };
239 if let Some(expr) = children
240 .iter()
241 .map(|x| {
242 Expression::PropertyReference(NamedReference::new(x, SmolStr::new_static("min-height")))
243 })
244 .reduce(|lhs, rhs| crate::builtin_macros::min_max_expression(lhs, rhs, MinMaxOp::Max))
245 {
246 elem.borrow_mut().bindings.insert("content-min-height".into(), RefCell::new(expr.into()));
247 };
248
249 elem.borrow_mut().children = std::iter::once(tabbar).chain(children).collect();
250}
251
252fn set_geometry_prop(
253 tab_widget: &ElementRc,
254 content: &ElementRc,
255 prop: &str,
256 diag: &mut BuildDiagnostics,
257) {
258 let old = content.borrow_mut().bindings.insert(
259 prop.into(),
260 RefCell::new(
261 Expression::PropertyReference(NamedReference::new(
262 tab_widget,
263 format_smolstr!("content-{}", prop),
264 ))
265 .into(),
266 ),
267 );
268 if let Some(old) = old.map(RefCell::into_inner) {
269 diag.push_error(
270 format!("The property '{prop}' cannot be set for Tabs inside a TabWidget"),
271 &old,
272 );
273 }
274}
275
276fn set_tabbar_geometry_prop(tab_widget: &ElementRc, tabbar: &ElementRc, prop: &str) {
277 tabbar.borrow_mut().bindings.insert(
278 prop.into(),
279 RefCell::new(
280 Expression::PropertyReference(NamedReference::new(
281 tab_widget,
282 format_smolstr!("tabbar-{}", prop),
283 ))
284 .into(),
285 ),
286 );
287}