// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 use crate::Cli; use i_slint_compiler::expression_tree::Expression; use i_slint_compiler::langtype::Type; use i_slint_compiler::object_tree::ElementRc; use i_slint_compiler::parser::{SyntaxKind, SyntaxNode}; use std::io::Write; pub(crate) fn fold_node( node: &SyntaxNode, file: &mut impl Write, state: &mut crate::State, args: &Cli, ) -> std::io::Result { let kind = node.kind(); if kind == SyntaxKind::Element { if state.lookup_change.scope.len() >= 2 { let elem = &state.lookup_change.scope[state.lookup_change.scope.len() - 1]; let parent = &state.lookup_change.scope[state.lookup_change.scope.len() - 2]; if !is_layout_base(parent) && !is_path(elem) && elem.borrow().is_legacy_syntax { let extend = elem.borrow().builtin_type().map_or(false, |b| { b.default_size_binding != i_slint_compiler::langtype::DefaultSizeBinding::ImplicitSize }); let new_props = format!( "{}{}", new_geometry_binding(elem, "x", "width", extend), new_geometry_binding(elem, "y", "height", extend) ); if !new_props.is_empty() { let mut seen_brace = false; for c in node.children_with_tokens() { if seen_brace { if c.kind() == SyntaxKind::Whitespace { crate::visit_node_or_token(c.clone(), file, state, args)?; } write!(file, "{new_props}")?; seen_brace = false; } else if c.kind() == SyntaxKind::LBrace { seen_brace = true; } crate::visit_node_or_token(c, file, state, args)?; } return Ok(true); } } } } Ok(false) } fn new_geometry_binding(elem: &ElementRc, pos_prop: &str, size_prop: &str, extend: bool) -> String { if elem.borrow().lookup_property(pos_prop).property_type != Type::LogicalLength { return String::default(); } if elem.borrow().is_binding_set(pos_prop, false) { return String::default(); } if extend && !elem.borrow().is_binding_set(size_prop, false) { return String::default(); } if let Some(b) = elem.borrow().bindings.get(size_prop) { if let Expression::Uncompiled(x) = &b.borrow().expression { let s = x.to_string(); if s.trim() == "100%;" || s.trim() == format!("parent.{size_prop};") { return String::default(); } } } format!("{pos_prop}:0;") } fn is_layout_base(elem: &ElementRc) -> bool { match &elem.borrow().base_type { i_slint_compiler::langtype::ElementType::Builtin(b) => { return matches!( b.name.as_str(), "GridLayout" | "HorizontalLayout" | "VerticalLayout" | "Row" | "Path" | "Dialog" ); } i_slint_compiler::langtype::ElementType::Component(c) => { if c.id == "ListView" { return true; } if let Some(ins) = &*c.child_insertion_point.borrow() { return is_layout_base(&ins.0); } else { return is_layout_base(&c.root_element); } } _ => return false, }; } fn is_path(elem: &ElementRc) -> bool { // Path's children are still considered in the Path for some reason, so just don't touch path elem.borrow().base_type.to_string() == "Path" }