mirror of
https://github.com/slint-ui/slint.git
synced 2025-09-28 21:04:47 +00:00
Also wrap langtype::Type::Struct in an Rc
This makes copying such types much cheaper and will allow us to intern common struct types in the future too. This further drops the sample cost for langtype.rs from ~6.6% down to 4.0%. We are now also able to share/intern common struct types. Before: ``` Time (mean ± σ): 1.073 s ± 0.021 s [User: 0.759 s, System: 0.215 s] Range (min … max): 1.034 s … 1.105 s 10 runs allocations: 3074261 ``` After: ``` Time (mean ± σ): 1.034 s ± 0.026 s [User: 0.733 s, System: 0.201 s] Range (min … max): 1.000 s … 1.078 s 10 runs allocations: 2917476 ```
This commit is contained in:
parent
efdecf0a13
commit
69c68b22b2
29 changed files with 302 additions and 259 deletions
|
@ -114,10 +114,11 @@ impl JsComponentCompiler {
|
|||
pub fn structs(&self, env: Env) -> HashMap<String, JsUnknown> {
|
||||
fn convert_type(env: &Env, ty: &Type) -> Option<(String, JsUnknown)> {
|
||||
match ty {
|
||||
Type::Struct { fields, name: Some(name), node: Some(_), .. } => {
|
||||
Type::Struct(s) if s.name.is_some() && s.node.is_some() => {
|
||||
let name = s.name.as_ref().unwrap();
|
||||
let struct_instance = to_js_unknown(
|
||||
env,
|
||||
&Value::Struct(slint_interpreter::Struct::from_iter(fields.iter().map(
|
||||
&Value::Struct(slint_interpreter::Struct::from_iter(s.fields.iter().map(
|
||||
|(name, field_type)| {
|
||||
(
|
||||
name.to_string(),
|
||||
|
|
|
@ -216,11 +216,11 @@ pub fn to_value(env: &Env, unknown: JsUnknown, typ: &Type) -> Result<Value> {
|
|||
Ok(Value::Image(Image::from_rgba8(pixel_buffer)))
|
||||
}
|
||||
}
|
||||
Type::Struct { fields, name: _, node: _, rust_attributes: _ } => {
|
||||
Type::Struct(s) => {
|
||||
let js_object = unknown.coerce_to_object()?;
|
||||
|
||||
Ok(Value::Struct(
|
||||
fields
|
||||
s.fields
|
||||
.iter()
|
||||
.map(|(pro_name, pro_ty)| {
|
||||
let prop: JsUnknown = js_object
|
||||
|
|
|
@ -153,9 +153,9 @@ impl CompilationResult {
|
|||
|
||||
fn convert_type(py: Python<'_>, ty: &Type) -> Option<(String, PyObject)> {
|
||||
match ty {
|
||||
Type::Struct { fields, name: Some(name), node: Some(_), .. } => {
|
||||
Type::Struct(s) if s.name.is_some() && s.node.is_some() => {
|
||||
let struct_instance = PyStruct::from(slint_interpreter::Struct::from_iter(
|
||||
fields.iter().map(|(name, field_type)| {
|
||||
s.fields.iter().map(|(name, field_type)| {
|
||||
(
|
||||
name.to_string(),
|
||||
slint_interpreter::default_value_for_type(field_type),
|
||||
|
@ -163,7 +163,10 @@ impl CompilationResult {
|
|||
}),
|
||||
));
|
||||
|
||||
return Some((name.to_string(), struct_instance.into_py(py)));
|
||||
return Some((
|
||||
s.name.as_ref().unwrap().to_string(),
|
||||
struct_instance.into_py(py),
|
||||
));
|
||||
}
|
||||
Type::Enumeration(_en) => {
|
||||
// TODO
|
||||
|
|
|
@ -380,13 +380,13 @@ fn to_debug_string(
|
|||
true_expr: Box::new(Expression::StringLiteral("true".into())),
|
||||
false_expr: Box::new(Expression::StringLiteral("false".into())),
|
||||
},
|
||||
Type::Struct { fields, .. } => {
|
||||
Type::Struct(s) => {
|
||||
let local_object = format_smolstr!(
|
||||
"debug_struct{}",
|
||||
COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
|
||||
);
|
||||
let mut string = None;
|
||||
for k in fields.keys() {
|
||||
for k in s.fields.keys() {
|
||||
let field_name = if string.is_some() {
|
||||
format_smolstr!(", {}: ", k)
|
||||
} else {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
|
||||
|
||||
use crate::diagnostics::{BuildDiagnostics, SourceLocation, Spanned};
|
||||
use crate::langtype::{BuiltinElement, EnumerationValue, Function, Type};
|
||||
use crate::langtype::{BuiltinElement, EnumerationValue, Function, Struct, Type};
|
||||
use crate::layout::Orientation;
|
||||
use crate::lookup::LookupCtx;
|
||||
use crate::object_tree::*;
|
||||
|
@ -175,7 +175,7 @@ impl BuiltinFunction {
|
|||
args: vec![Type::ElementReference],
|
||||
},
|
||||
BuiltinFunction::ColorRgbaStruct => Function {
|
||||
return_type: Type::Struct {
|
||||
return_type: Type::Struct(Rc::new(Struct {
|
||||
fields: IntoIterator::into_iter([
|
||||
(SmolStr::new_static("red"), Type::Int32),
|
||||
(SmolStr::new_static("green"), Type::Int32),
|
||||
|
@ -186,11 +186,11 @@ impl BuiltinFunction {
|
|||
name: Some("Color".into()),
|
||||
node: None,
|
||||
rust_attributes: None,
|
||||
},
|
||||
})),
|
||||
args: vec![Type::Color],
|
||||
},
|
||||
BuiltinFunction::ColorHsvaStruct => Function {
|
||||
return_type: Type::Struct {
|
||||
return_type: Type::Struct(Rc::new(Struct {
|
||||
fields: IntoIterator::into_iter([
|
||||
(SmolStr::new_static("hue"), Type::Float32),
|
||||
(SmolStr::new_static("saturation"), Type::Float32),
|
||||
|
@ -201,7 +201,7 @@ impl BuiltinFunction {
|
|||
name: Some("Color".into()),
|
||||
node: None,
|
||||
rust_attributes: None,
|
||||
},
|
||||
})),
|
||||
args: vec![Type::Color],
|
||||
},
|
||||
BuiltinFunction::ColorBrighter => {
|
||||
|
@ -221,7 +221,7 @@ impl BuiltinFunction {
|
|||
Function { return_type: Type::Brush, args: vec![Type::Brush, Type::Float32] }
|
||||
}
|
||||
BuiltinFunction::ImageSize => Function {
|
||||
return_type: Type::Struct {
|
||||
return_type: Type::Struct(Rc::new(Struct {
|
||||
fields: IntoIterator::into_iter([
|
||||
(SmolStr::new_static("width"), Type::Int32),
|
||||
(SmolStr::new_static("height"), Type::Int32),
|
||||
|
@ -230,7 +230,7 @@ impl BuiltinFunction {
|
|||
name: Some("Size".into()),
|
||||
node: None,
|
||||
rust_attributes: None,
|
||||
},
|
||||
})),
|
||||
args: vec![Type::Image],
|
||||
},
|
||||
BuiltinFunction::ArrayLength => {
|
||||
|
@ -767,9 +767,7 @@ impl Expression {
|
|||
.map_or(Type::Invalid, |e| model_inner_type(&e.model)),
|
||||
Expression::FunctionParameterReference { ty, .. } => ty.clone(),
|
||||
Expression::StructFieldAccess { base, name } => match base.ty() {
|
||||
Type::Struct { fields, .. } => {
|
||||
fields.get(name.as_str()).unwrap_or(&Type::Invalid).clone()
|
||||
}
|
||||
Type::Struct(s) => s.fields.get(name.as_str()).unwrap_or(&Type::Invalid).clone(),
|
||||
_ => Type::Invalid,
|
||||
},
|
||||
Expression::ArrayIndex { array, .. } => match array.ty() {
|
||||
|
@ -1181,13 +1179,12 @@ impl Expression {
|
|||
rhs: Box::new(Expression::NumberLiteral(0.01, Unit::None)),
|
||||
op: '*',
|
||||
},
|
||||
(
|
||||
ref from_ty @ Type::Struct { fields: ref left, .. },
|
||||
Type::Struct { fields: right, .. },
|
||||
) if left != right => {
|
||||
(ref from_ty @ Type::Struct(ref left), Type::Struct(ref right))
|
||||
if left.fields != right.fields =>
|
||||
{
|
||||
if let Expression::Struct { mut values, .. } = self {
|
||||
let mut new_values = HashMap::new();
|
||||
for (key, ty) in right {
|
||||
for (key, ty) in &right.fields {
|
||||
let (key, expression) = values.remove_entry(key).map_or_else(
|
||||
|| (key.clone(), Expression::default_value_for_type(ty)),
|
||||
|(k, e)| (k, e.maybe_convert_to(ty.clone(), node, diag)),
|
||||
|
@ -1198,8 +1195,8 @@ impl Expression {
|
|||
}
|
||||
let var_name = "tmpobj";
|
||||
let mut new_values = HashMap::new();
|
||||
for (key, ty) in right {
|
||||
let expression = if left.contains_key(key) {
|
||||
for (key, ty) in &right.fields {
|
||||
let expression = if left.fields.contains_key(key) {
|
||||
Expression::StructFieldAccess {
|
||||
base: Box::new(Expression::ReadLocalVariable {
|
||||
name: var_name.into(),
|
||||
|
@ -1290,11 +1287,11 @@ impl Expression {
|
|||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else if let (Type::Struct { fields, .. }, Expression::Struct { values, .. }) =
|
||||
} else if let (Type::Struct(struct_type), Expression::Struct { values, .. }) =
|
||||
(&target_type, &self)
|
||||
{
|
||||
// Also special case struct literal in case they contain array literal
|
||||
let mut fields = fields.clone();
|
||||
let mut fields = struct_type.fields.clone();
|
||||
let mut new_values = HashMap::new();
|
||||
for (f, v) in values {
|
||||
if let Some(t) = fields.remove(f) {
|
||||
|
@ -1371,9 +1368,10 @@ impl Expression {
|
|||
Type::Array(element_ty) => {
|
||||
Expression::Array { element_ty: (**element_ty).clone(), values: vec![] }
|
||||
}
|
||||
Type::Struct { fields, .. } => Expression::Struct {
|
||||
Type::Struct(s) => Expression::Struct {
|
||||
ty: ty.clone(),
|
||||
values: fields
|
||||
values: s
|
||||
.fields
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), Expression::default_value_for_type(v)))
|
||||
.collect(),
|
||||
|
|
|
@ -464,7 +464,7 @@ pub fn for_each_const_properties(component: &Rc<Component>, mut f: impl FnMut(&E
|
|||
.iter()
|
||||
.filter(|(_, x)| {
|
||||
x.property_type.is_property_type() &&
|
||||
!matches!( &x.property_type, crate::langtype::Type::Struct { name: Some(name), .. } if name.ends_with("::StateInfo"))
|
||||
!matches!( &x.property_type, crate::langtype::Type::Struct(s) if s.name.as_ref().map_or(false, |name| name.ends_with("::StateInfo")))
|
||||
})
|
||||
.map(|(k, _)| k.clone()),
|
||||
);
|
||||
|
|
|
@ -511,20 +511,20 @@ impl CppType for Type {
|
|||
Type::Rem => Some("float".into()),
|
||||
Type::Percent => Some("float".into()),
|
||||
Type::Bool => Some("bool".into()),
|
||||
Type::Struct { name: Some(name), node: Some(_), .. } => Some(ident(name)),
|
||||
Type::Struct { name: Some(name), node: None, .. } => {
|
||||
Some(if name.starts_with("slint::") {
|
||||
Type::Struct(s) => match (&s.name, &s.node) {
|
||||
(Some(name), Some(_)) => Some(ident(name)),
|
||||
(Some(name), None) => Some(if name.starts_with("slint::") {
|
||||
name.clone()
|
||||
} else {
|
||||
format_smolstr!("slint::cbindgen_private::{}", ident(name))
|
||||
})
|
||||
}
|
||||
Type::Struct { fields, .. } => {
|
||||
let elem = fields.values().map(|v| v.cpp_type()).collect::<Option<Vec<_>>>()?;
|
||||
|
||||
Some(format_smolstr!("std::tuple<{}>", elem.join(", ")))
|
||||
}
|
||||
}),
|
||||
_ => {
|
||||
let elem =
|
||||
s.fields.values().map(|v| v.cpp_type()).collect::<Option<Vec<_>>>()?;
|
||||
|
||||
Some(format_smolstr!("std::tuple<{}>", elem.join(", ")))
|
||||
}
|
||||
},
|
||||
Type::Array(i) => {
|
||||
Some(format_smolstr!("std::shared_ptr<slint::Model<{}>>", i.cpp_type()?))
|
||||
}
|
||||
|
@ -693,8 +693,13 @@ pub fn generate(
|
|||
|
||||
for ty in doc.used_types.borrow().structs_and_enums.iter() {
|
||||
match ty {
|
||||
Type::Struct { fields, name: Some(name), node: Some(node), .. } => {
|
||||
generate_struct(&mut file, name, fields, node);
|
||||
Type::Struct(s) if s.name.is_some() && s.node.is_some() => {
|
||||
generate_struct(
|
||||
&mut file,
|
||||
s.name.as_ref().unwrap(),
|
||||
&s.fields,
|
||||
s.node.as_ref().unwrap(),
|
||||
);
|
||||
}
|
||||
Type::Enumeration(en) => {
|
||||
generate_enum(&mut file, en);
|
||||
|
@ -2997,15 +3002,16 @@ fn compile_expression(expr: &llr::Expression, ctx: &EvaluationContext) -> String
|
|||
}
|
||||
Expression::ReadLocalVariable { name, .. } => ident(name).to_string(),
|
||||
Expression::StructFieldAccess { base, name } => match base.ty(ctx) {
|
||||
Type::Struct { fields, name : None, .. } => {
|
||||
let index = fields
|
||||
.keys()
|
||||
.position(|k| k == name)
|
||||
.expect("Expression::ObjectAccess: Cannot find a key in an object");
|
||||
format!("std::get<{}>({})", index, compile_expression(base, ctx))
|
||||
}
|
||||
Type::Struct{..} => {
|
||||
format!("{}.{}", compile_expression(base, ctx), ident(name))
|
||||
Type::Struct(s)=> {
|
||||
if s.name.is_none() {
|
||||
let index = s.fields
|
||||
.keys()
|
||||
.position(|k| k == name)
|
||||
.expect("Expression::ObjectAccess: Cannot find a key in an object");
|
||||
format!("std::get<{}>({})", index, compile_expression(base, ctx))
|
||||
} else {
|
||||
format!("{}.{}", compile_expression(base, ctx), ident(name))
|
||||
}
|
||||
}
|
||||
_ => panic!("Expression::ObjectAccess's base expression is not an Object type"),
|
||||
},
|
||||
|
@ -3037,11 +3043,11 @@ fn compile_expression(expr: &llr::Expression, ctx: &EvaluationContext) -> String
|
|||
(Type::Brush, Type::Color) => {
|
||||
format!("{}.color()", f)
|
||||
}
|
||||
(Type::Struct { .. }, Type::Struct{ fields, name: Some(_), ..}) => {
|
||||
(Type::Struct (_), Type::Struct(s)) if s.name.is_some() => {
|
||||
format!(
|
||||
"[&](const auto &o){{ {struct_name} s; {fields} return s; }}({obj})",
|
||||
struct_name = to.cpp_type().unwrap(),
|
||||
fields = fields.keys().enumerate().map(|(i, n)| format!("s.{} = std::get<{}>(o); ", ident(n), i)).join(""),
|
||||
fields = s.fields.keys().enumerate().map(|(i, n)| format!("s.{} = std::get<{}>(o); ", ident(n), i)).join(""),
|
||||
obj = f,
|
||||
)
|
||||
}
|
||||
|
@ -3056,7 +3062,7 @@ fn compile_expression(expr: &llr::Expression, ctx: &EvaluationContext) -> String
|
|||
.iter()
|
||||
.map(|path_elem_expr| {
|
||||
let (field_count, qualified_elem_type_name) = match path_elem_expr.ty(ctx) {
|
||||
Type::Struct{ fields, name: Some(name), .. } => (fields.len(), name),
|
||||
Type::Struct(s) if s.name.is_some() => (s.fields.len(), s.name.as_ref().unwrap().clone()),
|
||||
_ => unreachable!()
|
||||
};
|
||||
// Turn slint::private_api::PathLineTo into `LineTo`
|
||||
|
@ -3247,28 +3253,32 @@ fn compile_expression(expr: &llr::Expression, ctx: &EvaluationContext) -> String
|
|||
}
|
||||
}
|
||||
Expression::Struct { ty, values } => {
|
||||
if let Type::Struct{fields, name: None, ..} = ty {
|
||||
let mut elem = fields.iter().map(|(k, t)| {
|
||||
values
|
||||
.get(k)
|
||||
.map(|e| compile_expression(e, ctx))
|
||||
.map(|e| {
|
||||
// explicit conversion to avoid warning C4244 (possible loss of data) with MSVC
|
||||
if t.as_unit_product().is_some() { format!("{}({e})", t.cpp_type().unwrap()) } else {e}
|
||||
})
|
||||
.unwrap_or_else(|| "(Error: missing member in object)".to_owned())
|
||||
});
|
||||
format!("std::make_tuple({})", elem.join(", "))
|
||||
} else if let Type::Struct{ name: Some(_), .. } = ty {
|
||||
format!(
|
||||
"[&]({args}){{ {ty} o{{}}; {fields}return o; }}({vals})",
|
||||
args = (0..values.len()).map(|i| format!("const auto &a_{}", i)).join(", "),
|
||||
ty = ty.cpp_type().unwrap(),
|
||||
fields = values.keys().enumerate().map(|(i, f)| format!("o.{} = a_{}; ", ident(f), i)).join(""),
|
||||
vals = values.values().map(|e| compile_expression(e, ctx)).join(", "),
|
||||
)
|
||||
} else {
|
||||
match ty {
|
||||
Type::Struct(s) if s.name.is_none() => {
|
||||
let mut elem = s.fields.iter().map(|(k, t)| {
|
||||
values
|
||||
.get(k)
|
||||
.map(|e| compile_expression(e, ctx))
|
||||
.map(|e| {
|
||||
// explicit conversion to avoid warning C4244 (possible loss of data) with MSVC
|
||||
if t.as_unit_product().is_some() { format!("{}({e})", t.cpp_type().unwrap()) } else {e}
|
||||
})
|
||||
.unwrap_or_else(|| "(Error: missing member in object)".to_owned())
|
||||
});
|
||||
format!("std::make_tuple({})", elem.join(", "))
|
||||
},
|
||||
Type::Struct(_) => {
|
||||
format!(
|
||||
"[&]({args}){{ {ty} o{{}}; {fields}return o; }}({vals})",
|
||||
args = (0..values.len()).map(|i| format!("const auto &a_{}", i)).join(", "),
|
||||
ty = ty.cpp_type().unwrap(),
|
||||
fields = values.keys().enumerate().map(|(i, f)| format!("o.{} = a_{}; ", ident(f), i)).join(""),
|
||||
vals = values.values().map(|e| compile_expression(e, ctx)).join(", "),
|
||||
)
|
||||
},
|
||||
_ => {
|
||||
panic!("Expression::Object is not a Type::Object")
|
||||
}
|
||||
}
|
||||
}
|
||||
Expression::EasingCurve(EasingCurve::Linear) => "slint::cbindgen_private::EasingCurve()".into(),
|
||||
|
@ -3809,8 +3819,8 @@ fn generate_type_aliases(file: &mut File, doc: &Document) {
|
|||
Some((&export.0.name, &component.id))
|
||||
}
|
||||
Either::Right(ty) => match &ty {
|
||||
Type::Struct { name: Some(name), node: Some(_), .. } => {
|
||||
Some((&export.0.name, name))
|
||||
Type::Struct(s) if s.name.is_some() && s.node.is_some() => {
|
||||
Some((&export.0.name, s.name.as_ref().unwrap()))
|
||||
}
|
||||
Type::Enumeration(en) => Some((&export.0.name, &en.name)),
|
||||
_ => None,
|
||||
|
|
|
@ -13,7 +13,7 @@ Some convention used in the generated code:
|
|||
*/
|
||||
|
||||
use crate::expression_tree::{BuiltinFunction, EasingCurve, MinMaxOp, OperatorClass};
|
||||
use crate::langtype::{Enumeration, EnumerationValue, Type};
|
||||
use crate::langtype::{Enumeration, EnumerationValue, Struct, Type};
|
||||
use crate::layout::Orientation;
|
||||
use crate::llr::{
|
||||
self, EvaluationContext as llr_EvaluationContext, Expression, ParentCtx as llr_ParentCtx,
|
||||
|
@ -92,12 +92,16 @@ fn rust_primitive_type(ty: &Type) -> Option<proc_macro2::TokenStream> {
|
|||
Type::Percent => Some(quote!(f32)),
|
||||
Type::Bool => Some(quote!(bool)),
|
||||
Type::Image => Some(quote!(sp::Image)),
|
||||
Type::Struct { fields, name: None, .. } => {
|
||||
let elem = fields.values().map(rust_primitive_type).collect::<Option<Vec<_>>>()?;
|
||||
// This will produce a tuple
|
||||
Some(quote!((#(#elem,)*)))
|
||||
Type::Struct(s) => {
|
||||
if let Some(name) = &s.name {
|
||||
Some(struct_name_to_tokens(name))
|
||||
} else {
|
||||
let elem =
|
||||
s.fields.values().map(rust_primitive_type).collect::<Option<Vec<_>>>()?;
|
||||
// This will produce a tuple
|
||||
Some(quote!((#(#elem,)*)))
|
||||
}
|
||||
}
|
||||
Type::Struct { name: Some(name), .. } => Some(struct_name_to_tokens(name)),
|
||||
Type::Array(o) => {
|
||||
let inner = rust_primitive_type(o)?;
|
||||
Some(quote!(sp::ModelRc<#inner>))
|
||||
|
@ -154,9 +158,12 @@ pub fn generate(doc: &Document, compiler_config: &CompilerConfiguration) -> Toke
|
|||
.structs_and_enums
|
||||
.iter()
|
||||
.filter_map(|ty| match ty {
|
||||
Type::Struct { fields, name: Some(name), node: Some(_), rust_attributes } => {
|
||||
Some((ident(name), generate_struct(name, fields, rust_attributes)))
|
||||
}
|
||||
Type::Struct(s) => match s.as_ref() {
|
||||
Struct { fields, name: Some(name), node: Some(_), rust_attributes } => {
|
||||
Some((ident(name), generate_struct(name, fields, rust_attributes)))
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
Type::Enumeration(en) => Some((ident(&en.name), generate_enum(en))),
|
||||
_ => None,
|
||||
})
|
||||
|
@ -2146,13 +2153,13 @@ fn compile_expression(expr: &Expression, ctx: &EvaluationContext) -> TokenStream
|
|||
(Type::Brush, Type::Color) => {
|
||||
quote!(#f.color())
|
||||
}
|
||||
(Type::Struct { ref fields, .. }, Type::Struct { name: Some(n), .. }) => {
|
||||
let fields = fields.iter().enumerate().map(|(index, (name, _))| {
|
||||
(Type::Struct (lhs), Type::Struct (rhs)) if rhs.name.is_some() => {
|
||||
let fields = lhs.fields.iter().enumerate().map(|(index, (name, _))| {
|
||||
let index = proc_macro2::Literal::usize_unsuffixed(index);
|
||||
let name = ident(name);
|
||||
quote!(the_struct.#name = obj.#index as _;)
|
||||
});
|
||||
let id = struct_name_to_tokens(n);
|
||||
let id = struct_name_to_tokens(rhs.name.as_ref().unwrap());
|
||||
quote!({ let obj = #f; let mut the_struct = #id::default(); #(#fields)* the_struct })
|
||||
}
|
||||
(Type::Array(..), Type::PathData)
|
||||
|
@ -2166,7 +2173,7 @@ fn compile_expression(expr: &Expression, ctx: &EvaluationContext) -> TokenStream
|
|||
.iter()
|
||||
.map(|path_elem_expr|
|
||||
// Close{} is a struct with no fields in markup, and PathElement::Close has no fields
|
||||
if matches!(path_elem_expr, Expression::Struct { ty: Type::Struct { fields, .. }, .. } if fields.is_empty()) {
|
||||
if matches!(path_elem_expr, Expression::Struct { ty: Type::Struct (s), .. } if s.fields.is_empty()) {
|
||||
quote!(sp::PathElement::Close)
|
||||
} else {
|
||||
compile_expression(path_elem_expr, ctx)
|
||||
|
@ -2245,8 +2252,8 @@ fn compile_expression(expr: &Expression, ctx: &EvaluationContext) -> TokenStream
|
|||
quote! {args.#i.clone()}
|
||||
}
|
||||
Expression::StructFieldAccess { base, name } => match base.ty(ctx) {
|
||||
Type::Struct { fields, name: None, .. } => {
|
||||
let index = fields
|
||||
Type::Struct (s) if s.name.is_none() => {
|
||||
let index = s.fields
|
||||
.keys()
|
||||
.position(|k| k == name)
|
||||
.expect("Expression::StructFieldAccess: Cannot find a key in an object");
|
||||
|
@ -2428,18 +2435,18 @@ fn compile_expression(expr: &Expression, ctx: &EvaluationContext) -> TokenStream
|
|||
}
|
||||
}
|
||||
Expression::Struct { ty, values } => {
|
||||
if let Type::Struct { fields, name, .. } = ty {
|
||||
let elem = fields.keys().map(|k| values.get(k).map(|e| compile_expression(e, ctx)));
|
||||
if let Some(name) = name {
|
||||
if let Type::Struct (s) = ty {
|
||||
let elem = s.fields.keys().map(|k| values.get(k).map(|e| compile_expression(e, ctx)));
|
||||
if let Some(name) = &s.name {
|
||||
let name_tokens: TokenStream = struct_name_to_tokens(name.as_str());
|
||||
let keys = fields.keys().map(|k| ident(k));
|
||||
let keys = s.fields.keys().map(|k| ident(k));
|
||||
if name.starts_with("slint::private_api::") && name.ends_with("LayoutData") {
|
||||
quote!(#name_tokens{#(#keys: #elem as _,)*})
|
||||
} else {
|
||||
quote!({ let mut the_struct = #name_tokens::default(); #(the_struct.#keys = #elem as _;)* the_struct})
|
||||
}
|
||||
} else {
|
||||
let as_ = fields.values().map(|t| {
|
||||
let as_ = s.fields.values().map(|t| {
|
||||
if t.as_unit_product().is_some() {
|
||||
// number needs to be converted to the right things because intermediate
|
||||
// result might be f64 and that's usually not what the type of the tuple is in the end
|
||||
|
@ -3128,8 +3135,8 @@ fn generate_named_exports(doc: &Document) -> Vec<TokenStream> {
|
|||
Some((&export.0.name, &component.id))
|
||||
}
|
||||
Either::Right(ty) => match &ty {
|
||||
Type::Struct { name: Some(name), node: Some(_), .. } => {
|
||||
Some((&export.0.name, name))
|
||||
Type::Struct(s) if s.name.is_some() && s.node.is_some() => {
|
||||
Some((&export.0.name, s.name.as_ref().unwrap()))
|
||||
}
|
||||
Type::Enumeration(en) => Some((&export.0.name, &en.name)),
|
||||
_ => None,
|
||||
|
|
|
@ -52,16 +52,7 @@ pub enum Type {
|
|||
Brush,
|
||||
/// This is usually a model
|
||||
Array(Box<Type>),
|
||||
Struct {
|
||||
fields: BTreeMap<SmolStr, Type>,
|
||||
/// When declared in .slint as `struct Foo := { }`, then the name is "Foo"
|
||||
/// When there is no node, but there is a name, then it is a builtin type
|
||||
name: Option<SmolStr>,
|
||||
/// When declared in .slint, this is the node of the declaration.
|
||||
node: Option<syntax_nodes::ObjectType>,
|
||||
/// derived
|
||||
rust_attributes: Option<Vec<SmolStr>>,
|
||||
},
|
||||
Struct(Rc<Struct>),
|
||||
Enumeration(Rc<Enumeration>),
|
||||
|
||||
/// A type made up of the product of several "unit" types.
|
||||
|
@ -106,8 +97,8 @@ impl core::cmp::PartialEq for Type {
|
|||
Type::Easing => matches!(other, Type::Easing),
|
||||
Type::Brush => matches!(other, Type::Brush),
|
||||
Type::Array(a) => matches!(other, Type::Array(b) if a == b),
|
||||
Type::Struct { fields, name, node: _, rust_attributes: _ } => {
|
||||
matches!(other, Type::Struct{fields:f,name:n,node:_, rust_attributes: _ } if fields == f && name == n)
|
||||
Type::Struct(lhs) => {
|
||||
matches!(other, Type::Struct(rhs) if lhs.fields == rhs.fields && lhs.name == rhs.name)
|
||||
}
|
||||
Type::Enumeration(lhs) => matches!(other, Type::Enumeration(rhs) if lhs == rhs),
|
||||
Type::UnitProduct(a) => matches!(other, Type::UnitProduct(b) if a == b),
|
||||
|
@ -166,15 +157,17 @@ impl Display for Type {
|
|||
Type::Bool => write!(f, "bool"),
|
||||
Type::Model => write!(f, "model"),
|
||||
Type::Array(t) => write!(f, "[{}]", t),
|
||||
Type::Struct { name: Some(name), .. } => write!(f, "{}", name),
|
||||
Type::Struct { fields, name: None, .. } => {
|
||||
write!(f, "{{ ")?;
|
||||
for (k, v) in fields {
|
||||
write!(f, "{}: {},", k, v)?;
|
||||
Type::Struct(t) => {
|
||||
if let Some(name) = &t.name {
|
||||
write!(f, "{}", name)
|
||||
} else {
|
||||
write!(f, "{{ ")?;
|
||||
for (k, v) in &t.fields {
|
||||
write!(f, "{}: {},", k, v)?;
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
write!(f, "}}")
|
||||
}
|
||||
|
||||
Type::PathData => write!(f, "pathdata"),
|
||||
Type::Easing => write!(f, "easing"),
|
||||
Type::Brush => write!(f, "brush"),
|
||||
|
@ -281,9 +274,7 @@ impl Type {
|
|||
| (Type::Percent, Type::Float32)
|
||||
| (Type::Brush, Type::Color)
|
||||
| (Type::Color, Type::Brush) => true,
|
||||
(Type::Struct { fields: a, .. }, Type::Struct { fields: b, .. }) => {
|
||||
can_convert_struct(a, b)
|
||||
}
|
||||
(Type::Struct(a), Type::Struct(b)) => can_convert_struct(&a.fields, &b.fields),
|
||||
(Type::UnitProduct(u), o) => match o.as_unit_product() {
|
||||
Some(o) => unit_product_length_conversion(u.as_slice(), o.as_slice()).is_some(),
|
||||
None => false,
|
||||
|
@ -803,6 +794,18 @@ pub struct Function {
|
|||
pub args: Vec<Type>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Struct {
|
||||
pub fields: BTreeMap<SmolStr, Type>,
|
||||
/// When declared in .slint as `struct Foo := { }`, then the name is "Foo"
|
||||
/// When there is no node, but there is a name, then it is a builtin type
|
||||
pub name: Option<SmolStr>,
|
||||
/// When declared in .slint, this is the node of the declaration.
|
||||
pub node: Option<syntax_nodes::ObjectType>,
|
||||
/// derived
|
||||
pub rust_attributes: Option<Vec<SmolStr>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Enumeration {
|
||||
pub name: SmolStr,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
use crate::diagnostics::BuildDiagnostics;
|
||||
use crate::expression_tree::*;
|
||||
use crate::langtype::{ElementType, PropertyLookupResult, Type};
|
||||
use crate::langtype::{ElementType, PropertyLookupResult, Struct, Type};
|
||||
use crate::object_tree::{Component, ElementRc};
|
||||
|
||||
use smol_str::{format_smolstr, SmolStr};
|
||||
|
@ -487,7 +487,7 @@ impl BoxLayout {
|
|||
|
||||
/// The [`Type`] for a runtime LayoutInfo structure
|
||||
pub fn layout_info_type() -> Type {
|
||||
Type::Struct {
|
||||
Type::Struct(Rc::new(Struct {
|
||||
fields: ["min", "max", "preferred"]
|
||||
.iter()
|
||||
.map(|s| (SmolStr::new_static(s), Type::LogicalLength))
|
||||
|
@ -500,7 +500,7 @@ pub fn layout_info_type() -> Type {
|
|||
name: Some("slint::private_api::LayoutInfo".into()),
|
||||
node: None,
|
||||
rust_attributes: None,
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
/// Get the implicit layout info of a particular element
|
||||
|
|
|
@ -228,9 +228,10 @@ impl Expression {
|
|||
values: vec![],
|
||||
as_model: true,
|
||||
},
|
||||
Type::Struct { fields, .. } => Expression::Struct {
|
||||
Type::Struct(s) => Expression::Struct {
|
||||
ty: ty.clone(),
|
||||
values: fields
|
||||
values: s
|
||||
.fields
|
||||
.iter()
|
||||
.map(|(k, v)| Some((k.clone(), Expression::default_value_for_type(v)?)))
|
||||
.collect::<Option<_>>()?,
|
||||
|
@ -257,7 +258,7 @@ impl Expression {
|
|||
Self::StoreLocalVariable { .. } => Type::Void,
|
||||
Self::ReadLocalVariable { ty, .. } => ty.clone(),
|
||||
Self::StructFieldAccess { base, name } => match base.ty(ctx) {
|
||||
Type::Struct { fields, .. } => fields[name].clone(),
|
||||
Type::Struct(s) => s.fields[name].clone(),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Self::ArrayIndex { array, .. } => match array.ty(ctx) {
|
||||
|
|
|
@ -12,7 +12,7 @@ use smol_str::{format_smolstr, SmolStr};
|
|||
|
||||
use super::lower_to_item_tree::{LoweredElement, LoweredSubComponentMapping, LoweringState};
|
||||
use super::{Animation, PropertyReference};
|
||||
use crate::langtype::{EnumerationValue, Type};
|
||||
use crate::langtype::{EnumerationValue, Struct, Type};
|
||||
use crate::layout::Orientation;
|
||||
use crate::llr::Expression as llr_Expression;
|
||||
use crate::namedreference::NamedReference;
|
||||
|
@ -260,8 +260,8 @@ fn lower_assignment(
|
|||
tree_Expression::ReadLocalVariable { name: unique_name, ty: ty.clone() };
|
||||
let mut values = HashMap::new();
|
||||
match &ty {
|
||||
Type::Struct { fields, .. } => {
|
||||
for field in fields.keys() {
|
||||
Type::Struct(s) => {
|
||||
for field in s.fields.keys() {
|
||||
let e = if field != name {
|
||||
tree_Expression::StructFieldAccess {
|
||||
base: lower_base.clone().into(),
|
||||
|
@ -423,12 +423,12 @@ pub fn lower_animation(a: &PropertyAnimation, ctx: &ExpressionContext<'_>) -> An
|
|||
}
|
||||
|
||||
fn animation_ty() -> Type {
|
||||
Type::Struct {
|
||||
Type::Struct(Rc::new(Struct {
|
||||
fields: animation_fields().collect(),
|
||||
name: Some("slint::private_api::PropertyAnimation".into()),
|
||||
node: None,
|
||||
rust_attributes: None,
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
match a {
|
||||
|
@ -456,7 +456,7 @@ pub fn lower_animation(a: &PropertyAnimation, ctx: &ExpressionContext<'_>) -> An
|
|||
}
|
||||
let result = llr_Expression::Struct {
|
||||
// This is going to be a tuple
|
||||
ty: Type::Struct {
|
||||
ty: Type::Struct(Rc::new(Struct {
|
||||
fields: IntoIterator::into_iter([
|
||||
(SmolStr::new_static("0"), animation_ty),
|
||||
// The type is an instant, which does not exist in our type system
|
||||
|
@ -466,7 +466,7 @@ pub fn lower_animation(a: &PropertyAnimation, ctx: &ExpressionContext<'_>) -> An
|
|||
name: None,
|
||||
node: None,
|
||||
rust_attributes: None,
|
||||
},
|
||||
})),
|
||||
values: IntoIterator::into_iter([
|
||||
(SmolStr::new_static("0"), get_anim),
|
||||
(
|
||||
|
@ -684,13 +684,13 @@ fn box_layout_data(
|
|||
let repeater_count =
|
||||
layout.elems.iter().filter(|i| i.element.borrow().repeated.is_some()).count();
|
||||
|
||||
let element_ty = Type::Struct {
|
||||
let element_ty = Type::Struct(Rc::new(Struct {
|
||||
fields: IntoIterator::into_iter([("constraint".into(), crate::layout::layout_info_type())])
|
||||
.collect(),
|
||||
name: Some("BoxLayoutCellData".into()),
|
||||
node: None,
|
||||
rust_attributes: None,
|
||||
};
|
||||
}));
|
||||
|
||||
if repeater_count == 0 {
|
||||
let cells = llr_Expression::Array {
|
||||
|
@ -767,7 +767,7 @@ fn grid_layout_cell_data(
|
|||
}
|
||||
|
||||
pub(super) fn grid_layout_cell_data_ty() -> Type {
|
||||
Type::Struct {
|
||||
Type::Struct(Rc::new(Struct {
|
||||
fields: IntoIterator::into_iter([
|
||||
(SmolStr::new_static("col_or_row"), Type::Int32),
|
||||
(SmolStr::new_static("span"), Type::Int32),
|
||||
|
@ -777,7 +777,7 @@ pub(super) fn grid_layout_cell_data_ty() -> Type {
|
|||
name: Some("GridLayoutCellData".into()),
|
||||
node: None,
|
||||
rust_attributes: None,
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
fn generate_layout_padding_and_spacing(
|
||||
|
@ -833,7 +833,7 @@ pub fn get_layout_info(
|
|||
};
|
||||
let ty = crate::layout::layout_info_type();
|
||||
let fields = match &ty {
|
||||
Type::Struct { fields, .. } => fields,
|
||||
Type::Struct(s) => &s.fields,
|
||||
_ => panic!(),
|
||||
};
|
||||
let mut values = fields
|
||||
|
@ -869,12 +869,12 @@ fn compile_path(path: &crate::expression_tree::Path, ctx: &ExpressionContext) ->
|
|||
fn llr_path_elements(elements: Vec<llr_Expression>) -> llr_Expression {
|
||||
llr_Expression::Cast {
|
||||
from: llr_Expression::Array {
|
||||
element_ty: Type::Struct {
|
||||
element_ty: Type::Struct(Rc::new(Struct {
|
||||
fields: Default::default(),
|
||||
name: Some("PathElement".into()),
|
||||
node: None,
|
||||
rust_attributes: None,
|
||||
},
|
||||
})),
|
||||
values: elements,
|
||||
as_model: false,
|
||||
}
|
||||
|
@ -888,7 +888,7 @@ fn compile_path(path: &crate::expression_tree::Path, ctx: &ExpressionContext) ->
|
|||
let converted_elements = elements
|
||||
.iter()
|
||||
.map(|element| {
|
||||
let element_type = Type::Struct {
|
||||
let element_type = Type::Struct(Rc::new(Struct {
|
||||
fields: element
|
||||
.element_type
|
||||
.properties
|
||||
|
@ -898,7 +898,7 @@ fn compile_path(path: &crate::expression_tree::Path, ctx: &ExpressionContext) ->
|
|||
name: element.element_type.native_class.cpp_type.clone(),
|
||||
node: None,
|
||||
rust_attributes: None,
|
||||
};
|
||||
}));
|
||||
|
||||
llr_Expression::Struct {
|
||||
ty: element_type,
|
||||
|
@ -941,7 +941,7 @@ fn compile_path(path: &crate::expression_tree::Path, ctx: &ExpressionContext) ->
|
|||
|
||||
llr_Expression::Cast {
|
||||
from: llr_Expression::Struct {
|
||||
ty: Type::Struct {
|
||||
ty: Type::Struct(Rc::new(Struct {
|
||||
fields: IntoIterator::into_iter([
|
||||
(SmolStr::new_static("events"), Type::Array(event_type.clone().into())),
|
||||
(SmolStr::new_static("points"), Type::Array(point_type.clone().into())),
|
||||
|
@ -950,7 +950,7 @@ fn compile_path(path: &crate::expression_tree::Path, ctx: &ExpressionContext) ->
|
|||
name: None,
|
||||
node: None,
|
||||
rust_attributes: None,
|
||||
},
|
||||
})),
|
||||
values: IntoIterator::into_iter([
|
||||
(
|
||||
SmolStr::new_static("events"),
|
||||
|
@ -994,12 +994,12 @@ pub fn make_struct(
|
|||
}
|
||||
|
||||
llr_Expression::Struct {
|
||||
ty: Type::Struct {
|
||||
ty: Type::Struct(Rc::new(Struct {
|
||||
fields,
|
||||
name: Some(format_smolstr!("slint::private_api::{name}")),
|
||||
node: None,
|
||||
rust_attributes: None,
|
||||
},
|
||||
})),
|
||||
values,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use by_address::ByAddress;
|
|||
|
||||
use super::lower_expression::ExpressionContext;
|
||||
use crate::expression_tree::Expression as tree_Expression;
|
||||
use crate::langtype::{ElementType, Type};
|
||||
use crate::langtype::{ElementType, Struct, Type};
|
||||
use crate::llr::item_tree::*;
|
||||
use crate::namedreference::NamedReference;
|
||||
use crate::object_tree::{self, Component, ElementRc, PropertyAnalysis, PropertyVisibility};
|
||||
|
@ -395,7 +395,7 @@ fn lower_sub_component(
|
|||
|
||||
let is_state_info = matches!(
|
||||
e.borrow().lookup_property(p).property_type,
|
||||
Type::Struct { name: Some(name), .. } if name.ends_with("::StateInfo")
|
||||
Type::Struct(s) if s.name.as_ref().map_or(false, |name| name.ends_with("::StateInfo"))
|
||||
);
|
||||
|
||||
sub_component.property_init.push((
|
||||
|
@ -554,7 +554,7 @@ fn lower_geometry(
|
|||
.insert(f.into(), super::Expression::PropertyReference(ctx.map_property_reference(v)));
|
||||
}
|
||||
super::Expression::Struct {
|
||||
ty: Type::Struct { fields, name: None, node: None, rust_attributes: None },
|
||||
ty: Type::Struct(Rc::new(Struct { fields, name: None, node: None, rust_attributes: None })),
|
||||
values,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -954,8 +954,8 @@ impl LookupObject for Expression {
|
|||
match self {
|
||||
Expression::ElementReference(e) => e.upgrade().unwrap().for_each_entry(ctx, f),
|
||||
_ => match self.ty() {
|
||||
Type::Struct { fields, .. } => {
|
||||
for name in fields.keys() {
|
||||
Type::Struct(s) => {
|
||||
for name in s.fields.keys() {
|
||||
if let Some(r) = f(
|
||||
name,
|
||||
Expression::StructFieldAccess {
|
||||
|
@ -988,7 +988,7 @@ impl LookupObject for Expression {
|
|||
match self {
|
||||
Expression::ElementReference(e) => e.upgrade().unwrap().lookup(ctx, name),
|
||||
_ => match self.ty() {
|
||||
Type::Struct { fields, .. } => fields.contains_key(name).then(|| {
|
||||
Type::Struct(s) => s.fields.contains_key(name).then(|| {
|
||||
LookupResult::from(Expression::StructFieldAccess {
|
||||
base: Box::new(self.clone()),
|
||||
name: name.into(),
|
||||
|
|
|
@ -11,7 +11,8 @@ use crate::diagnostics::{BuildDiagnostics, SourceLocation, Spanned};
|
|||
use crate::expression_tree::{self, BindingExpression, Expression, Unit};
|
||||
use crate::langtype::EnumerationValue;
|
||||
use crate::langtype::{
|
||||
BuiltinElement, BuiltinPropertyDefault, Callback, Enumeration, Function, NativeClass, Type,
|
||||
BuiltinElement, BuiltinPropertyDefault, Callback, Enumeration, Function, NativeClass, Struct,
|
||||
Type,
|
||||
};
|
||||
use crate::langtype::{ElementType, PropertyLookupResult};
|
||||
use crate::layout::{LayoutConstraints, Orientation};
|
||||
|
@ -90,14 +91,14 @@ impl Document {
|
|||
local_registry: &mut TypeRegister,
|
||||
inner_types: &mut Vec<Type>| {
|
||||
let rust_attributes = n.AtRustAttr().map(|child| vec![child.text().to_smolstr()]);
|
||||
let mut ty =
|
||||
type_struct_from_node(n.ObjectType(), diag, local_registry, rust_attributes);
|
||||
if let Type::Struct { name, .. } = &mut ty {
|
||||
*name = parser::identifier_text(&n.DeclaredIdentifier());
|
||||
} else {
|
||||
assert!(diag.has_errors());
|
||||
return;
|
||||
}
|
||||
let ty = type_struct_from_node(
|
||||
n.ObjectType(),
|
||||
diag,
|
||||
local_registry,
|
||||
rust_attributes,
|
||||
parser::identifier_text(&n.DeclaredIdentifier()),
|
||||
);
|
||||
assert!(matches!(ty, Type::Struct(_)));
|
||||
local_registry.insert_type(ty.clone());
|
||||
inner_types.push(ty);
|
||||
};
|
||||
|
@ -1882,7 +1883,7 @@ pub fn type_from_node(
|
|||
}
|
||||
prop_type
|
||||
} else if let Some(object_node) = node.ObjectType() {
|
||||
type_struct_from_node(object_node, diag, tr, None)
|
||||
type_struct_from_node(object_node, diag, tr, None, None)
|
||||
} else if let Some(array_node) = node.ArrayType() {
|
||||
Type::Array(Box::new(type_from_node(array_node.Type(), diag, tr)))
|
||||
} else {
|
||||
|
@ -1897,6 +1898,7 @@ pub fn type_struct_from_node(
|
|||
diag: &mut BuildDiagnostics,
|
||||
tr: &TypeRegister,
|
||||
rust_attributes: Option<Vec<SmolStr>>,
|
||||
name: Option<SmolStr>,
|
||||
) -> Type {
|
||||
let fields = object_node
|
||||
.ObjectTypeMember()
|
||||
|
@ -1907,7 +1909,7 @@ pub fn type_struct_from_node(
|
|||
)
|
||||
})
|
||||
.collect();
|
||||
Type::Struct { fields, name: None, node: Some(object_node), rust_attributes }
|
||||
Type::Struct(Rc::new(Struct { fields, name, node: Some(object_node), rust_attributes }))
|
||||
}
|
||||
|
||||
fn animation_element_from_node(
|
||||
|
|
|
@ -61,15 +61,17 @@ fn collect_types_in_component(root_component: &Rc<Component>, hash: &mut BTreeMa
|
|||
/// it are placed before in the vector
|
||||
fn sort_types(hash: &mut BTreeMap<SmolStr, Type>, vec: &mut Vec<Type>, key: &str) {
|
||||
let ty = if let Some(ty) = hash.remove(key) { ty } else { return };
|
||||
if let Type::Struct { fields, name: Some(name), .. } = &ty {
|
||||
if name.contains("::") {
|
||||
// This is a builtin type.
|
||||
// FIXME! there should be a better way to handle builtin struct
|
||||
return;
|
||||
}
|
||||
if let Type::Struct(s) = &ty {
|
||||
if let Some(name) = &s.name {
|
||||
if name.contains("::") {
|
||||
// This is a builtin type.
|
||||
// FIXME! there should be a better way to handle builtin struct
|
||||
return;
|
||||
}
|
||||
|
||||
for sub_ty in fields.values() {
|
||||
visit_declared_type(sub_ty, &mut |name, _| sort_types(hash, vec, name));
|
||||
for sub_ty in s.fields.values() {
|
||||
visit_declared_type(sub_ty, &mut |name, _| sort_types(hash, vec, name));
|
||||
}
|
||||
}
|
||||
}
|
||||
vec.push(ty)
|
||||
|
@ -78,13 +80,13 @@ fn sort_types(hash: &mut BTreeMap<SmolStr, Type>, vec: &mut Vec<Type>, key: &str
|
|||
/// Will call the `visitor` for every named struct or enum that is not builtin
|
||||
fn visit_declared_type(ty: &Type, visitor: &mut impl FnMut(&SmolStr, &Type)) {
|
||||
match ty {
|
||||
Type::Struct { fields, name, node, .. } => {
|
||||
if node.is_some() {
|
||||
if let Some(struct_name) = name.as_ref() {
|
||||
Type::Struct(s) => {
|
||||
if s.node.is_some() {
|
||||
if let Some(struct_name) = s.name.as_ref() {
|
||||
visitor(struct_name, ty);
|
||||
}
|
||||
}
|
||||
for sub_ty in fields.values() {
|
||||
for sub_ty in s.fields.values() {
|
||||
visit_declared_type(sub_ty, visitor);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
use crate::diagnostics::BuildDiagnostics;
|
||||
use crate::expression_tree::*;
|
||||
use crate::langtype::ElementType;
|
||||
use crate::langtype::Type;
|
||||
use crate::langtype::{Struct, Type};
|
||||
use crate::object_tree::*;
|
||||
use crate::EmbedResourcesKind;
|
||||
use smol_str::SmolStr;
|
||||
|
@ -152,7 +152,7 @@ fn compile_path_from_string_literal(
|
|||
let path = builder.build();
|
||||
|
||||
let event_enum = crate::typeregister::BUILTIN_ENUMS.with(|e| e.PathEvent.clone());
|
||||
let point_type = Type::Struct {
|
||||
let point_type = Type::Struct(Rc::new(Struct {
|
||||
fields: IntoIterator::into_iter([
|
||||
(SmolStr::new_static("x"), Type::Float32),
|
||||
(SmolStr::new_static("y"), Type::Float32),
|
||||
|
@ -161,7 +161,7 @@ fn compile_path_from_string_literal(
|
|||
name: Some("slint::private_api::Point".into()),
|
||||
node: None,
|
||||
rust_attributes: None,
|
||||
};
|
||||
}));
|
||||
|
||||
let mut points = Vec::new();
|
||||
let events = path
|
||||
|
|
|
@ -23,7 +23,12 @@ fn simplify_expression(expr: &mut Expression) -> bool {
|
|||
match expr {
|
||||
Expression::PropertyReference(nr) => {
|
||||
if nr.is_constant()
|
||||
&& !matches!(nr.ty(), Type::Struct { name: Some(name), .. } if name.ends_with("::StateInfo"))
|
||||
&& !match nr.ty() {
|
||||
Type::Struct(s) => {
|
||||
s.name.as_ref().map_or(false, |name| name.ends_with("::StateInfo"))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
{
|
||||
// Inline the constant value
|
||||
if let Some(result) = extract_constant_property_reference(nr) {
|
||||
|
|
|
@ -264,8 +264,9 @@ fn merge_explicit_constraints(
|
|||
name: unique_name.clone(),
|
||||
value: Box::new(std::mem::take(expr)),
|
||||
};
|
||||
let Type::Struct { fields, .. } = &ty else { unreachable!() };
|
||||
let mut values = fields
|
||||
let Type::Struct(s) = &ty else { unreachable!() };
|
||||
let mut values = s
|
||||
.fields
|
||||
.keys()
|
||||
.map(|p| {
|
||||
(
|
||||
|
|
|
@ -21,7 +21,7 @@ pub fn lower_states(
|
|||
diag: &mut BuildDiagnostics,
|
||||
) {
|
||||
let state_info_type = tr.lookup("StateInfo");
|
||||
assert!(matches!(state_info_type, Type::Struct { name: Some(_), .. }));
|
||||
assert!(matches!(state_info_type, Type::Struct(ref s) if s.name.is_some()));
|
||||
recurse_elem(&component.root_element, &(), &mut |elem, _| {
|
||||
lower_state_in_element(elem, &state_info_type, diag)
|
||||
});
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
|
||||
use smol_str::{format_smolstr, SmolStr};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::expression_tree::Expression;
|
||||
use crate::langtype::Type;
|
||||
use crate::langtype::{Struct, Type};
|
||||
|
||||
pub fn remove_return(doc: &crate::object_tree::Document) {
|
||||
doc.visit_all_used_components(|component| {
|
||||
|
@ -431,7 +432,12 @@ fn make_struct(it: impl Iterator<Item = (&'static str, Type, Expression)>) -> Ex
|
|||
codeblock_with_expr(
|
||||
voids,
|
||||
Expression::Struct {
|
||||
ty: Type::Struct { fields, name: None, node: None, rust_attributes: None },
|
||||
ty: Type::Struct(Rc::new(Struct {
|
||||
fields,
|
||||
name: None,
|
||||
node: None,
|
||||
rust_attributes: None,
|
||||
})),
|
||||
values,
|
||||
},
|
||||
)
|
||||
|
@ -440,13 +446,13 @@ fn make_struct(it: impl Iterator<Item = (&'static str, Type, Expression)>) -> Ex
|
|||
/// Given an expression `from` of type Struct, convert to another type struct with more fields
|
||||
/// Add missing members in `from`
|
||||
fn convert_struct(from: Expression, to: Type) -> Expression {
|
||||
let Type::Struct { fields, .. } = &to else {
|
||||
let Type::Struct(s) = &to else {
|
||||
assert_eq!(to, Type::Invalid);
|
||||
return Expression::Invalid;
|
||||
};
|
||||
if let Expression::Struct { mut values, .. } = from {
|
||||
let mut new_values = HashMap::new();
|
||||
for (key, ty) in fields {
|
||||
for (key, ty) in &s.fields {
|
||||
let (key, expression) = values
|
||||
.remove_entry(key)
|
||||
.unwrap_or_else(|| (key.clone(), Expression::default_value_for_type(ty)));
|
||||
|
@ -457,12 +463,12 @@ fn convert_struct(from: Expression, to: Type) -> Expression {
|
|||
let var_name = "tmpobj";
|
||||
let from_ty = from.ty();
|
||||
let mut new_values = HashMap::new();
|
||||
let Type::Struct { fields: form_fields, .. } = &from_ty else {
|
||||
let Type::Struct(from_s) = &from_ty else {
|
||||
assert_eq!(from_ty, Type::Invalid);
|
||||
return Expression::Invalid;
|
||||
};
|
||||
for (key, ty) in fields {
|
||||
let expression = if form_fields.contains_key(key) {
|
||||
for (key, ty) in &s.fields {
|
||||
let expression = if from_s.fields.contains_key(key) {
|
||||
Expression::StructFieldAccess {
|
||||
base: Box::new(Expression::ReadLocalVariable {
|
||||
name: var_name.into(),
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
use crate::diagnostics::{BuildDiagnostics, Spanned};
|
||||
use crate::expression_tree::*;
|
||||
use crate::langtype::{ElementType, Type};
|
||||
use crate::langtype::{ElementType, Struct, Type};
|
||||
use crate::lookup::{LookupCtx, LookupObject, LookupResult};
|
||||
use crate::object_tree::*;
|
||||
use crate::parser::{identifier_text, syntax_nodes, NodeOrToken, SyntaxKind, SyntaxNode};
|
||||
|
@ -1205,12 +1205,12 @@ impl Expression {
|
|||
)
|
||||
})
|
||||
.collect();
|
||||
let ty = Type::Struct {
|
||||
let ty = Type::Struct(Rc::new(Struct {
|
||||
fields: values.iter().map(|(k, v)| (k.clone(), v.ty())).collect(),
|
||||
name: None,
|
||||
node: None,
|
||||
rust_attributes: None,
|
||||
};
|
||||
}));
|
||||
Expression::Struct { ty, values }
|
||||
}
|
||||
|
||||
|
@ -1268,41 +1268,34 @@ impl Expression {
|
|||
expr_ty
|
||||
} else {
|
||||
match (target_type, expr_ty) {
|
||||
(
|
||||
Type::Struct {
|
||||
fields: mut result_fields,
|
||||
name: result_name,
|
||||
node: result_node,
|
||||
rust_attributes,
|
||||
},
|
||||
Type::Struct {
|
||||
fields: elem_fields,
|
||||
name: elem_name,
|
||||
node: elem_node,
|
||||
rust_attributes: derived,
|
||||
},
|
||||
) => {
|
||||
for (elem_name, elem_ty) in elem_fields.into_iter() {
|
||||
match result_fields.entry(elem_name) {
|
||||
(Type::Struct(ref result), Type::Struct(ref elem)) => {
|
||||
let mut fields = result.fields.clone();
|
||||
for (elem_name, elem_ty) in elem.fields.iter() {
|
||||
match fields.entry(elem_name.clone()) {
|
||||
std::collections::btree_map::Entry::Vacant(free_entry) => {
|
||||
free_entry.insert(elem_ty);
|
||||
free_entry.insert(elem_ty.clone());
|
||||
}
|
||||
std::collections::btree_map::Entry::Occupied(
|
||||
mut existing_field,
|
||||
) => {
|
||||
*existing_field.get_mut() =
|
||||
Self::common_target_type_for_type_list(
|
||||
[existing_field.get().clone(), elem_ty].into_iter(),
|
||||
[existing_field.get().clone(), elem_ty.clone()]
|
||||
.into_iter(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Type::Struct {
|
||||
name: result_name.or(elem_name),
|
||||
fields: result_fields,
|
||||
node: result_node.or(elem_node),
|
||||
rust_attributes: rust_attributes.or(derived),
|
||||
}
|
||||
Type::Struct(Rc::new(Struct {
|
||||
name: result.name.as_ref().or(elem.name.as_ref()).cloned(),
|
||||
fields,
|
||||
node: result.node.as_ref().or(elem.node.as_ref()).cloned(),
|
||||
rust_attributes: result
|
||||
.rust_attributes
|
||||
.as_ref()
|
||||
.or(elem.rust_attributes.as_ref())
|
||||
.cloned(),
|
||||
}))
|
||||
}
|
||||
(Type::Array(lhs), Type::Array(rhs)) => Type::Array(if *lhs == Type::Void {
|
||||
rhs
|
||||
|
|
|
@ -11,7 +11,7 @@ use std::rc::Rc;
|
|||
use crate::expression_tree::BuiltinFunction;
|
||||
use crate::langtype::{
|
||||
BuiltinElement, BuiltinPropertyDefault, BuiltinPropertyInfo, Callback, ElementType,
|
||||
Enumeration, PropertyLookupResult, Type,
|
||||
Enumeration, PropertyLookupResult, Struct, Type,
|
||||
};
|
||||
use crate::object_tree::{Component, PropertyVisibility};
|
||||
use crate::typeloader;
|
||||
|
@ -340,14 +340,14 @@ impl TypeRegister {
|
|||
}
|
||||
}
|
||||
)*) => { $(
|
||||
let $Name = Type::Struct {
|
||||
let $Name = Type::Struct(Rc::new(Struct{
|
||||
fields: BTreeMap::from([
|
||||
$((stringify!($pub_field).replace_smolstr("_", "-"), map_type!($pub_type, $pub_type))),*
|
||||
]),
|
||||
name: Some(format_smolstr!("{}", $inner_name)),
|
||||
node: None,
|
||||
rust_attributes: None,
|
||||
};
|
||||
}));
|
||||
register.insert_type_with_name(maybe_clone!($Name, $Name), SmolStr::new(stringify!($Name)));
|
||||
)* };
|
||||
}
|
||||
|
@ -585,29 +585,35 @@ impl TypeRegister {
|
|||
}
|
||||
|
||||
pub fn logical_point_type() -> Type {
|
||||
Type::Struct {
|
||||
fields: IntoIterator::into_iter([
|
||||
(SmolStr::new_static("x"), Type::LogicalLength),
|
||||
(SmolStr::new_static("y"), Type::LogicalLength),
|
||||
])
|
||||
.collect(),
|
||||
name: Some("slint::LogicalPosition".into()),
|
||||
node: None,
|
||||
rust_attributes: None,
|
||||
thread_local! {
|
||||
static TYPE: Type = Type::Struct(Rc::new(Struct {
|
||||
fields: IntoIterator::into_iter([
|
||||
(SmolStr::new_static("x"), Type::LogicalLength),
|
||||
(SmolStr::new_static("y"), Type::LogicalLength),
|
||||
])
|
||||
.collect(),
|
||||
name: Some("slint::LogicalPosition".into()),
|
||||
node: None,
|
||||
rust_attributes: None,
|
||||
}));
|
||||
}
|
||||
TYPE.with(|t| t.clone())
|
||||
}
|
||||
|
||||
pub fn font_metrics_type() -> Type {
|
||||
Type::Struct {
|
||||
fields: IntoIterator::into_iter([
|
||||
(SmolStr::new_static("ascent"), Type::LogicalLength),
|
||||
(SmolStr::new_static("descent"), Type::LogicalLength),
|
||||
(SmolStr::new_static("x-height"), Type::LogicalLength),
|
||||
(SmolStr::new_static("cap-height"), Type::LogicalLength),
|
||||
])
|
||||
.collect(),
|
||||
name: Some("slint::private_api::FontMetrics".into()),
|
||||
node: None,
|
||||
rust_attributes: None,
|
||||
thread_local! {
|
||||
static TYPE: Type = Type::Struct(Rc::new(Struct {
|
||||
fields: IntoIterator::into_iter([
|
||||
(SmolStr::new_static("ascent"), Type::LogicalLength),
|
||||
(SmolStr::new_static("descent"), Type::LogicalLength),
|
||||
(SmolStr::new_static("x-height"), Type::LogicalLength),
|
||||
(SmolStr::new_static("cap-height"), Type::LogicalLength),
|
||||
])
|
||||
.collect(),
|
||||
name: Some("slint::private_api::FontMetrics".into()),
|
||||
node: None,
|
||||
rust_attributes: None,
|
||||
}));
|
||||
}
|
||||
TYPE.with(|t| t.clone())
|
||||
}
|
||||
|
|
|
@ -2007,6 +2007,7 @@ fn component_definition_model_properties() {
|
|||
|
||||
#[test]
|
||||
fn lang_type_to_value_type() {
|
||||
use i_slint_compiler::langtype::Struct as LangStruct;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
assert_eq!(ValueType::from(LangType::Void), ValueType::Void);
|
||||
|
@ -2024,12 +2025,12 @@ fn lang_type_to_value_type() {
|
|||
assert_eq!(ValueType::from(LangType::Array(Box::new(LangType::Void))), ValueType::Model);
|
||||
assert_eq!(ValueType::from(LangType::Bool), ValueType::Bool);
|
||||
assert_eq!(
|
||||
ValueType::from(LangType::Struct {
|
||||
ValueType::from(LangType::Struct(Rc::new(LangStruct {
|
||||
fields: BTreeMap::default(),
|
||||
name: None,
|
||||
node: None,
|
||||
rust_attributes: None
|
||||
}),
|
||||
}))),
|
||||
ValueType::Struct
|
||||
);
|
||||
assert_eq!(ValueType::from(LangType::Image), ValueType::Image);
|
||||
|
|
|
@ -915,8 +915,8 @@ pub async fn load(
|
|||
Some((&export.0.name, &component.id))
|
||||
}
|
||||
Either::Right(ty) => match &ty {
|
||||
Type::Struct { name: Some(name), node: Some(_), .. } => {
|
||||
Some((&export.0.name, name))
|
||||
Type::Struct(s) if s.name.is_some() && s.node.is_some() => {
|
||||
Some((&export.0.name, s.name.as_ref().unwrap()))
|
||||
}
|
||||
Type::Enumeration(en) => Some((&export.0.name, &en.name)),
|
||||
_ => None,
|
||||
|
@ -1185,10 +1185,12 @@ pub(crate) fn generate_item_tree<'id>(
|
|||
Type::Image => property_info::<i_slint_core::graphics::Image>(),
|
||||
Type::Bool => property_info::<bool>(),
|
||||
Type::ComponentFactory => property_info::<ComponentFactory>(),
|
||||
Type::Struct { name: Some(name), .. } if name.ends_with("::StateInfo") => {
|
||||
Type::Struct(s)
|
||||
if s.name.as_ref().map_or(false, |name| name.ends_with("::StateInfo")) =>
|
||||
{
|
||||
property_info::<i_slint_core::properties::StateInfo>()
|
||||
}
|
||||
Type::Struct { .. } => property_info::<Value>(),
|
||||
Type::Struct(_) => property_info::<Value>(),
|
||||
Type::Array(_) => property_info::<Value>(),
|
||||
Type::Easing => property_info::<i_slint_core::animations::EasingCurve>(),
|
||||
Type::Percent => property_info::<f32>(),
|
||||
|
@ -1577,7 +1579,7 @@ pub fn instantiate(
|
|||
} else if let Some(PropertiesWithinComponent { offset, prop: prop_info, .. }) =
|
||||
description.custom_properties.get(prop_name).filter(|_| is_root)
|
||||
{
|
||||
let is_state_info = matches!(property_type, Type::Struct { name: Some(name), .. } if name.ends_with("::StateInfo"));
|
||||
let is_state_info = matches!(property_type, Type::Struct (s) if s.name.as_ref().map_or(false, |name| name.ends_with("::StateInfo")));
|
||||
if is_state_info {
|
||||
let prop = Pin::new_unchecked(
|
||||
&*(instance_ref.as_ptr().add(*offset)
|
||||
|
|
|
@ -1445,8 +1445,8 @@ fn check_value_type(value: &Value, ty: &Type) -> bool {
|
|||
Type::Array(inner) => {
|
||||
matches!(value, Value::Model(m) if m.iter().all(|v| check_value_type(&v, inner)))
|
||||
}
|
||||
Type::Struct { fields, .. } => {
|
||||
matches!(value, Value::Struct(str) if str.iter().all(|(k, v)| fields.get(k).map_or(false, |ty| check_value_type(v, ty))))
|
||||
Type::Struct(s) => {
|
||||
matches!(value, Value::Struct(str) if str.iter().all(|(k, v)| s.fields.get(k).map_or(false, |ty| check_value_type(v, ty))))
|
||||
}
|
||||
Type::Enumeration(en) => {
|
||||
matches!(value, Value::EnumerationValue(name, _) if name == en.name.as_str())
|
||||
|
@ -1688,8 +1688,8 @@ pub fn default_value_for_type(ty: &Type) -> Value {
|
|||
Type::Image => Value::Image(Default::default()),
|
||||
Type::Bool => Value::Bool(false),
|
||||
Type::Callback { .. } => Value::Void,
|
||||
Type::Struct { fields, .. } => Value::Struct(
|
||||
fields
|
||||
Type::Struct(s) => Value::Struct(
|
||||
s.fields
|
||||
.iter()
|
||||
.map(|(n, t)| (n.to_string(), default_value_for_type(t)))
|
||||
.collect::<Struct>(),
|
||||
|
|
|
@ -1015,12 +1015,12 @@ fn get_document_symbols(
|
|||
.collect::<Vec<_>>();
|
||||
|
||||
r.extend(inner_types.iter().filter_map(|c| match c {
|
||||
Type::Struct { name: Some(name), node: Some(node), .. } => Some(DocumentSymbol {
|
||||
range: util::node_to_lsp_range(node.parent().as_ref()?),
|
||||
Type::Struct(s) if s.name.is_some() && s.node.is_some() => Some(DocumentSymbol {
|
||||
range: util::node_to_lsp_range(s.node.as_ref().unwrap().parent().as_ref()?),
|
||||
selection_range: util::node_to_lsp_range(
|
||||
&node.parent()?.child_node(SyntaxKind::DeclaredIdentifier)?,
|
||||
&s.node.as_ref().unwrap().parent()?.child_node(SyntaxKind::DeclaredIdentifier)?,
|
||||
),
|
||||
name: name.to_string(),
|
||||
name: s.name.as_ref().unwrap().to_string(),
|
||||
kind: lsp_types::SymbolKind::STRUCT,
|
||||
..ds.clone()
|
||||
}),
|
||||
|
|
|
@ -61,7 +61,9 @@ pub fn goto_definition(
|
|||
|
||||
fn goto_type(ty: &Type) -> Option<GotoDefinitionResponse> {
|
||||
match ty {
|
||||
Type::Struct { node: Some(node), .. } => goto_node(node.parent().as_ref()?),
|
||||
Type::Struct(s) if s.node.is_some() => {
|
||||
goto_node(s.node.as_ref().unwrap().parent().as_ref()?)
|
||||
}
|
||||
Type::Enumeration(e) => goto_node(e.node.as_ref()?),
|
||||
_ => None,
|
||||
}
|
||||
|
|
|
@ -405,11 +405,11 @@ fn load_data(
|
|||
_ => slint_interpreter::Value::Void,
|
||||
},
|
||||
serde_json::Value::Object(obj) => match t {
|
||||
i_slint_compiler::langtype::Type::Struct { fields, .. } => obj
|
||||
i_slint_compiler::langtype::Type::Struct(s) => obj
|
||||
.iter()
|
||||
.filter_map(|(k, v)| {
|
||||
let k = k.to_smolstr();
|
||||
match fields.get(&k) {
|
||||
match s.fields.get(&k) {
|
||||
Some(t) => Some((k.to_string(), from_json(t, v))),
|
||||
None => {
|
||||
eprintln!("Warning: ignoring unknown property: {}", k);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue