mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-01 14:21:16 +00:00
Move Type and related concepts in a different module
Leaving only the TypeRegister in the typeregister module
This commit is contained in:
parent
76b7f1aef6
commit
27a6ff1227
24 changed files with 624 additions and 610 deletions
|
@ -9,7 +9,7 @@
|
||||||
LICENSE END */
|
LICENSE END */
|
||||||
|
|
||||||
use neon::prelude::*;
|
use neon::prelude::*;
|
||||||
use sixtyfps_compilerlib::typeregister::Type;
|
use sixtyfps_compilerlib::langtype::Type;
|
||||||
use sixtyfps_corelib::model::Model;
|
use sixtyfps_corelib::model::Model;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
LICENSE END */
|
LICENSE END */
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use neon::prelude::*;
|
use neon::prelude::*;
|
||||||
use sixtyfps_compilerlib::typeregister::Type;
|
use sixtyfps_compilerlib::langtype::Type;
|
||||||
use sixtyfps_corelib::Resource;
|
use sixtyfps_corelib::Resource;
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -150,7 +150,7 @@ fn create<'cx>(
|
||||||
|
|
||||||
fn to_eval_value<'cx>(
|
fn to_eval_value<'cx>(
|
||||||
val: Handle<'cx, JsValue>,
|
val: Handle<'cx, JsValue>,
|
||||||
ty: sixtyfps_compilerlib::typeregister::Type,
|
ty: sixtyfps_compilerlib::langtype::Type,
|
||||||
cx: &mut impl Context<'cx>,
|
cx: &mut impl Context<'cx>,
|
||||||
persistent_context: &persistent_context::PersistentContext<'cx>,
|
persistent_context: &persistent_context::PersistentContext<'cx>,
|
||||||
) -> NeonResult<sixtyfps_interpreter::Value> {
|
) -> NeonResult<sixtyfps_interpreter::Value> {
|
||||||
|
|
|
@ -8,9 +8,9 @@
|
||||||
Please contact info@sixtyfps.io for more information.
|
Please contact info@sixtyfps.io for more information.
|
||||||
LICENSE END */
|
LICENSE END */
|
||||||
use crate::diagnostics::{BuildDiagnostics, Spanned, SpannedWithSourceFile};
|
use crate::diagnostics::{BuildDiagnostics, Spanned, SpannedWithSourceFile};
|
||||||
|
use crate::langtype::{BuiltinElement, EnumerationValue, Type};
|
||||||
use crate::object_tree::*;
|
use crate::object_tree::*;
|
||||||
use crate::parser::SyntaxNodeWithSourceFile;
|
use crate::parser::SyntaxNodeWithSourceFile;
|
||||||
use crate::typeregister::{BuiltinElement, EnumerationValue, Type};
|
|
||||||
use core::cell::RefCell;
|
use core::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
|
@ -133,7 +133,7 @@ pub fn build_array_helper(
|
||||||
|
|
||||||
pub fn is_flickable(e: &ElementRc) -> bool {
|
pub fn is_flickable(e: &ElementRc) -> bool {
|
||||||
match &e.borrow().base_type {
|
match &e.borrow().base_type {
|
||||||
crate::typeregister::Type::Native(n) if n.class_name == "Flickable" => true,
|
crate::langtype::Type::Native(n) if n.class_name == "Flickable" => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -186,9 +186,9 @@ use crate::diagnostics::{BuildDiagnostics, CompilerDiagnostic, Level, Spanned};
|
||||||
use crate::expression_tree::{
|
use crate::expression_tree::{
|
||||||
BuiltinFunction, EasingCurve, Expression, ExpressionSpanned, NamedReference,
|
BuiltinFunction, EasingCurve, Expression, ExpressionSpanned, NamedReference,
|
||||||
};
|
};
|
||||||
|
use crate::langtype::Type;
|
||||||
use crate::layout::{gen::LayoutItemCodeGen, Layout, LayoutElement, LayoutGeometry};
|
use crate::layout::{gen::LayoutItemCodeGen, Layout, LayoutElement, LayoutGeometry};
|
||||||
use crate::object_tree::{Component, Document, Element, ElementRc, RepeatedElementInfo};
|
use crate::object_tree::{Component, Document, Element, ElementRc, RepeatedElementInfo};
|
||||||
use crate::typeregister::Type;
|
|
||||||
use cpp_ast::*;
|
use cpp_ast::*;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
|
@ -14,9 +14,9 @@ use crate::diagnostics::{BuildDiagnostics, CompilerDiagnostic, Level, Spanned};
|
||||||
use crate::expression_tree::{
|
use crate::expression_tree::{
|
||||||
BuiltinFunction, EasingCurve, Expression, NamedReference, OperatorClass, Path,
|
BuiltinFunction, EasingCurve, Expression, NamedReference, OperatorClass, Path,
|
||||||
};
|
};
|
||||||
|
use crate::langtype::Type;
|
||||||
use crate::layout::{gen::LayoutItemCodeGen, Layout, LayoutElement, LayoutGeometry};
|
use crate::layout::{gen::LayoutItemCodeGen, Layout, LayoutElement, LayoutGeometry};
|
||||||
use crate::object_tree::{Component, Document, ElementRc};
|
use crate::object_tree::{Component, Document, ElementRc};
|
||||||
use crate::typeregister::Type;
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
578
sixtyfps_compiler/langtype.rs
Normal file
578
sixtyfps_compiler/langtype.rs
Normal file
|
@ -0,0 +1,578 @@
|
||||||
|
/* LICENSE BEGIN
|
||||||
|
This file is part of the SixtyFPS Project -- https://sixtyfps.io
|
||||||
|
Copyright (c) 2020 Olivier Goffart <olivier.goffart@sixtyfps.io>
|
||||||
|
Copyright (c) 2020 Simon Hausmann <simon.hausmann@sixtyfps.io>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
This file is also available under commercial licensing terms.
|
||||||
|
Please contact info@sixtyfps.io for more information.
|
||||||
|
LICENSE END */
|
||||||
|
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||||
|
use std::{fmt::Display, rc::Rc};
|
||||||
|
|
||||||
|
use crate::expression_tree::{Expression, Unit};
|
||||||
|
use crate::object_tree::Component;
|
||||||
|
use crate::typeregister::TypeRegister;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Type {
|
||||||
|
/// Correspond to an uninitialized type, or an error
|
||||||
|
Invalid,
|
||||||
|
/// The type of an expression that return nothing
|
||||||
|
Void,
|
||||||
|
Component(Rc<crate::object_tree::Component>),
|
||||||
|
Builtin(Rc<BuiltinElement>),
|
||||||
|
Native(Rc<NativeClass>),
|
||||||
|
|
||||||
|
Signal {
|
||||||
|
args: Vec<Type>,
|
||||||
|
},
|
||||||
|
Function {
|
||||||
|
return_type: Box<Type>,
|
||||||
|
args: Vec<Type>,
|
||||||
|
},
|
||||||
|
|
||||||
|
// Other property types:
|
||||||
|
Float32,
|
||||||
|
Int32,
|
||||||
|
String,
|
||||||
|
Color,
|
||||||
|
Duration,
|
||||||
|
Length,
|
||||||
|
LogicalLength,
|
||||||
|
Percent,
|
||||||
|
Resource,
|
||||||
|
Bool,
|
||||||
|
Model,
|
||||||
|
PathElements,
|
||||||
|
Easing,
|
||||||
|
|
||||||
|
Array(Box<Type>),
|
||||||
|
Object(BTreeMap<String, Type>),
|
||||||
|
|
||||||
|
Enumeration(Rc<Enumeration>),
|
||||||
|
EnumerationValue(EnumerationValue),
|
||||||
|
|
||||||
|
ElementReference,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::cmp::PartialEq for Type {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
match self {
|
||||||
|
Type::Invalid => matches!(other, Type::Invalid),
|
||||||
|
Type::Void => matches!(other, Type::Void),
|
||||||
|
Type::Component(a) => matches!(other, Type::Component(b) if Rc::ptr_eq(a, b)),
|
||||||
|
Type::Builtin(a) => matches!(other, Type::Builtin(b) if Rc::ptr_eq(a, b)),
|
||||||
|
Type::Native(a) => matches!(other, Type::Native(b) if Rc::ptr_eq(a, b)),
|
||||||
|
Type::Signal { args: a } => matches!(other, Type::Signal { args: b } if a == b),
|
||||||
|
Type::Function { return_type: lhs_rt, args: lhs_args } => {
|
||||||
|
matches!(other, Type::Function { return_type: rhs_rt, args: rhs_args } if lhs_rt == rhs_rt && lhs_args == rhs_args)
|
||||||
|
}
|
||||||
|
Type::Float32 => matches!(other, Type::Float32),
|
||||||
|
Type::Int32 => matches!(other, Type::Int32),
|
||||||
|
Type::String => matches!(other, Type::String),
|
||||||
|
Type::Color => matches!(other, Type::Color),
|
||||||
|
Type::Duration => matches!(other, Type::Duration),
|
||||||
|
Type::Length => matches!(other, Type::Length),
|
||||||
|
Type::LogicalLength => matches!(other, Type::LogicalLength),
|
||||||
|
Type::Percent => matches!(other, Type::Percent),
|
||||||
|
Type::Resource => matches!(other, Type::Resource),
|
||||||
|
Type::Bool => matches!(other, Type::Bool),
|
||||||
|
Type::Model => matches!(other, Type::Model),
|
||||||
|
Type::PathElements => matches!(other, Type::PathElements),
|
||||||
|
Type::Easing => matches!(other, Type::Easing),
|
||||||
|
Type::Array(a) => matches!(other, Type::Array(b) if a == b),
|
||||||
|
Type::Object(a) => matches!(other, Type::Object(b) if a == b),
|
||||||
|
Type::Enumeration(lhs) => matches!(other, Type::Enumeration(rhs) if lhs == rhs),
|
||||||
|
Type::EnumerationValue(lhs) => {
|
||||||
|
matches!(other, Type::EnumerationValue(rhs) if lhs == rhs)
|
||||||
|
}
|
||||||
|
Type::ElementReference => matches!(other, Type::ElementReference),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Type {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Type::Invalid => write!(f, "<error>"),
|
||||||
|
Type::Void => write!(f, "void"),
|
||||||
|
Type::Component(c) => c.id.fmt(f),
|
||||||
|
Type::Builtin(b) => b.native_class.class_name.fmt(f),
|
||||||
|
Type::Native(b) => b.class_name.fmt(f),
|
||||||
|
Type::Signal { args } => {
|
||||||
|
write!(f, "signal")?;
|
||||||
|
if !args.is_empty() {
|
||||||
|
write!(f, "(")?;
|
||||||
|
for (i, arg) in args.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
write!(f, ",")?;
|
||||||
|
}
|
||||||
|
write!(f, "{}", arg)?;
|
||||||
|
}
|
||||||
|
write!(f, ")")?
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Type::Function { return_type, args } => {
|
||||||
|
write!(f, "function(")?;
|
||||||
|
for (i, arg) in args.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
write!(f, ",")?;
|
||||||
|
}
|
||||||
|
write!(f, "{}", arg)?;
|
||||||
|
}
|
||||||
|
write!(f, ") -> {}", return_type)
|
||||||
|
}
|
||||||
|
Type::Float32 => write!(f, "float"),
|
||||||
|
Type::Int32 => write!(f, "int"),
|
||||||
|
Type::String => write!(f, "string"),
|
||||||
|
Type::Duration => write!(f, "duration"),
|
||||||
|
Type::Length => write!(f, "length"),
|
||||||
|
Type::LogicalLength => write!(f, "logical_length"),
|
||||||
|
Type::Percent => write!(f, "percent"),
|
||||||
|
Type::Color => write!(f, "color"),
|
||||||
|
Type::Resource => write!(f, "resource"),
|
||||||
|
Type::Bool => write!(f, "bool"),
|
||||||
|
Type::Model => write!(f, "model"),
|
||||||
|
Type::Array(t) => write!(f, "[{}]", t),
|
||||||
|
Type::Object(t) => {
|
||||||
|
write!(f, "{{ ")?;
|
||||||
|
for (k, v) in t {
|
||||||
|
write!(f, "{}: {},", k, v)?;
|
||||||
|
}
|
||||||
|
write!(f, "}}")
|
||||||
|
}
|
||||||
|
Type::PathElements => write!(f, "pathelements"),
|
||||||
|
Type::Easing => write!(f, "easing"),
|
||||||
|
Type::Enumeration(enumeration) => write!(f, "enum {}", enumeration.name),
|
||||||
|
Type::EnumerationValue(value) => {
|
||||||
|
write!(f, "enum {}::{}", value.enumeration.name, value.to_string())
|
||||||
|
}
|
||||||
|
Type::ElementReference => write!(f, "element ref"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Type {
|
||||||
|
pub fn is_object_type(&self) -> bool {
|
||||||
|
matches!(self, Self::Component(_) | Self::Builtin(_))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// valid type for properties
|
||||||
|
pub fn is_property_type(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Float32
|
||||||
|
| Self::Int32
|
||||||
|
| Self::String
|
||||||
|
| Self::Color
|
||||||
|
| Self::Duration
|
||||||
|
| Self::Length
|
||||||
|
| Self::LogicalLength
|
||||||
|
| Self::Percent
|
||||||
|
| Self::Resource
|
||||||
|
| Self::Bool
|
||||||
|
| Self::Model
|
||||||
|
| Self::Easing
|
||||||
|
| Self::Enumeration(_)
|
||||||
|
| Self::ElementReference
|
||||||
|
| Self::Object(_)
|
||||||
|
| Self::Array(_) => true,
|
||||||
|
Self::Component(c) => c.root_element.borrow().base_type == Type::Void,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ok_for_public_api(&self) -> bool {
|
||||||
|
// Duration and Easing don't have good types for public API exposure yet.
|
||||||
|
!matches!(self, Self::Duration | Self::Easing)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lookup_property(&self, name: &str) -> Type {
|
||||||
|
match self {
|
||||||
|
Type::Component(c) => c.root_element.borrow().lookup_property(name),
|
||||||
|
Type::Builtin(b) => b.properties.get(name).cloned().unwrap_or_else(|| {
|
||||||
|
if b.is_non_item_type {
|
||||||
|
Type::Invalid
|
||||||
|
} else {
|
||||||
|
crate::typeregister::reserved_property(name)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
Type::Native(n) => n.lookup_property(name).unwrap_or_default(),
|
||||||
|
_ => Type::Invalid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lookup_type_for_child_element(
|
||||||
|
&self,
|
||||||
|
name: &str,
|
||||||
|
tr: &TypeRegister,
|
||||||
|
) -> Result<Type, String> {
|
||||||
|
match self {
|
||||||
|
Type::Component(component) => {
|
||||||
|
return component
|
||||||
|
.root_element
|
||||||
|
.borrow()
|
||||||
|
.base_type
|
||||||
|
.lookup_type_for_child_element(name, tr)
|
||||||
|
}
|
||||||
|
Type::Builtin(builtin) => {
|
||||||
|
if let Some(child_type) = builtin.additional_accepted_child_types.get(name) {
|
||||||
|
return Ok(child_type.clone());
|
||||||
|
}
|
||||||
|
if builtin.disallow_global_types_as_child_elements {
|
||||||
|
let mut valid_children: Vec<_> =
|
||||||
|
builtin.additional_accepted_child_types.keys().cloned().collect();
|
||||||
|
valid_children.sort();
|
||||||
|
|
||||||
|
return Err(format!(
|
||||||
|
"{} is not allowed within {}. Only {} are valid children",
|
||||||
|
name,
|
||||||
|
builtin.native_class.class_name,
|
||||||
|
valid_children.join(" ")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
tr.lookup_element(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lookup_member_function(&self, name: &str) -> Expression {
|
||||||
|
match self {
|
||||||
|
Type::Builtin(builtin) => builtin.member_functions.get(name).unwrap().clone(),
|
||||||
|
_ => Expression::Invalid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assume this is a builtin type, panic if it isn't
|
||||||
|
pub fn as_builtin(&self) -> &BuiltinElement {
|
||||||
|
match self {
|
||||||
|
Type::Builtin(b) => &b,
|
||||||
|
Type::Component(_) => panic!("This should not happen because of inlining"),
|
||||||
|
_ => panic!("invalid type"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assume this is a builtin type, panic if it isn't
|
||||||
|
pub fn as_native(&self) -> &NativeClass {
|
||||||
|
match self {
|
||||||
|
Type::Native(b) => &b,
|
||||||
|
Type::Component(_) => {
|
||||||
|
panic!("This should not happen because of native class resolution")
|
||||||
|
}
|
||||||
|
_ => panic!("invalid type"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Assime it is a Component, panic if it isn't
|
||||||
|
pub fn as_component(&self) -> &Rc<crate::object_tree::Component> {
|
||||||
|
match self {
|
||||||
|
Type::Component(c) => c,
|
||||||
|
_ => panic!("should be a component because of the repeater_component pass"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return true if the type can be converted to the other type
|
||||||
|
pub fn can_convert(&self, other: &Self) -> bool {
|
||||||
|
let can_convert_object = |a: &BTreeMap<String, Type>, b: &BTreeMap<String, Type>| {
|
||||||
|
// the object `b` has property that the object `a` doesn't
|
||||||
|
let mut has_more_property = false;
|
||||||
|
for (k, v) in b {
|
||||||
|
match a.get(k) {
|
||||||
|
Some(t) if !t.can_convert(v) => return false,
|
||||||
|
None => has_more_property = true,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if has_more_property {
|
||||||
|
// we should reject the conversion if `a` has property that `b` doesn't have
|
||||||
|
if a.keys().any(|k| !b.contains_key(k)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
};
|
||||||
|
let can_convert_object_to_component = |a: &BTreeMap<String, Type>, c: &Component| {
|
||||||
|
let root_element = c.root_element.borrow();
|
||||||
|
if root_element.base_type != Type::Void {
|
||||||
|
//component is not a struct
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (k, v) in &root_element.property_declarations {
|
||||||
|
if !a.get(k).map_or(false, |t| t.can_convert(&v.property_type)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
|
match (self, other) {
|
||||||
|
(a, b) if a == b => true,
|
||||||
|
(_, Type::Invalid)
|
||||||
|
| (_, Type::Void)
|
||||||
|
| (Type::Float32, Type::Int32)
|
||||||
|
| (Type::Float32, Type::String)
|
||||||
|
| (Type::Int32, Type::Float32)
|
||||||
|
| (Type::Int32, Type::String)
|
||||||
|
| (Type::Array(_), Type::Model)
|
||||||
|
| (Type::Float32, Type::Model)
|
||||||
|
| (Type::Int32, Type::Model)
|
||||||
|
| (Type::Length, Type::LogicalLength)
|
||||||
|
| (Type::LogicalLength, Type::Length)
|
||||||
|
| (Type::Percent, Type::Float32) => true,
|
||||||
|
(Type::Object(a), Type::Object(b)) if can_convert_object(a, b) => true,
|
||||||
|
(Type::Object(a), Type::Component(c)) if can_convert_object_to_component(a, c) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collect_contextual_types(
|
||||||
|
&self,
|
||||||
|
context_restricted_types: &mut HashMap<String, HashSet<String>>,
|
||||||
|
) {
|
||||||
|
let builtin = match self {
|
||||||
|
Type::Builtin(ty) => ty,
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
for (accepted_child_type_name, accepted_child_type) in
|
||||||
|
builtin.additional_accepted_child_types.iter()
|
||||||
|
{
|
||||||
|
context_restricted_types
|
||||||
|
.entry(accepted_child_type_name.clone())
|
||||||
|
.or_default()
|
||||||
|
.insert(builtin.native_class.class_name.clone());
|
||||||
|
|
||||||
|
accepted_child_type.collect_contextual_types(context_restricted_types);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If this is a number type which should be used with an unit, this returns the default unit
|
||||||
|
/// otherwise, returns None
|
||||||
|
pub fn default_unit(&self) -> Option<Unit> {
|
||||||
|
match self {
|
||||||
|
Type::Duration => Some(Unit::Ms),
|
||||||
|
Type::Length => Some(Unit::Phx),
|
||||||
|
Type::LogicalLength => Some(Unit::Px),
|
||||||
|
Type::Percent => Some(Unit::Percent),
|
||||||
|
Type::Invalid => None,
|
||||||
|
Type::Void => None,
|
||||||
|
Type::Component(_) => None,
|
||||||
|
Type::Builtin(_) => None,
|
||||||
|
Type::Native(_) => None,
|
||||||
|
Type::Signal { .. } => None,
|
||||||
|
Type::Function { .. } => None,
|
||||||
|
Type::Float32 => None,
|
||||||
|
Type::Int32 => None,
|
||||||
|
Type::String => None,
|
||||||
|
Type::Color => None,
|
||||||
|
Type::Resource => None,
|
||||||
|
Type::Bool => None,
|
||||||
|
Type::Model => None,
|
||||||
|
Type::PathElements => None,
|
||||||
|
Type::Easing => None,
|
||||||
|
Type::Array(_) => None,
|
||||||
|
Type::Object(_) => None,
|
||||||
|
Type::Enumeration(_) => None,
|
||||||
|
Type::EnumerationValue(_) => None,
|
||||||
|
Type::ElementReference => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Type {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Invalid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct NativeClass {
|
||||||
|
pub parent: Option<Rc<NativeClass>>,
|
||||||
|
pub class_name: String,
|
||||||
|
pub vtable_symbol: String,
|
||||||
|
pub properties: HashMap<String, Type>,
|
||||||
|
pub cpp_type: Option<String>,
|
||||||
|
pub rust_type_constructor: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NativeClass {
|
||||||
|
pub fn new(class_name: &str) -> Self {
|
||||||
|
let vtable_symbol = format!("{}VTable", class_name);
|
||||||
|
Self {
|
||||||
|
class_name: class_name.into(),
|
||||||
|
vtable_symbol,
|
||||||
|
properties: Default::default(),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_properties(
|
||||||
|
class_name: &str,
|
||||||
|
properties: impl IntoIterator<Item = (String, Type)>,
|
||||||
|
) -> Self {
|
||||||
|
let mut class = Self::new(class_name);
|
||||||
|
class.properties = properties.into_iter().collect();
|
||||||
|
class
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn property_count(&self) -> usize {
|
||||||
|
self.properties.len() + self.parent.clone().map(|p| p.property_count()).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn local_property_iter(&self) -> impl Iterator<Item = (&String, &Type)> {
|
||||||
|
self.properties.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn visit_class_hierarchy(self: Rc<Self>, mut visitor: impl FnMut(&Rc<Self>)) {
|
||||||
|
visitor(&self);
|
||||||
|
if let Some(parent_class) = &self.parent {
|
||||||
|
parent_class.clone().visit_class_hierarchy(visitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lookup_property(&self, name: &str) -> Option<Type> {
|
||||||
|
if let Some(ty) = self.properties.get(name) {
|
||||||
|
Some(ty.clone())
|
||||||
|
} else if let Some(parent_class) = &self.parent {
|
||||||
|
parent_class.lookup_property(name)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lookup_property_distance(self: Rc<Self>, name: &str) -> (usize, Rc<Self>) {
|
||||||
|
let mut distance = 0;
|
||||||
|
let mut class = self.clone();
|
||||||
|
loop {
|
||||||
|
if class.properties.contains_key(name) {
|
||||||
|
return (distance, class);
|
||||||
|
}
|
||||||
|
distance += 1;
|
||||||
|
class = class.parent.as_ref().unwrap().clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select_minimal_class_based_on_property_usage<'a>(
|
||||||
|
self: Rc<Self>,
|
||||||
|
properties_used: impl Iterator<Item = &'a String>,
|
||||||
|
) -> Rc<Self> {
|
||||||
|
let (_min_distance, minimal_class) = properties_used.fold(
|
||||||
|
(std::usize::MAX, self.clone()),
|
||||||
|
|(current_distance, current_class), prop_name| {
|
||||||
|
let (prop_distance, prop_class) = self.clone().lookup_property_distance(&prop_name);
|
||||||
|
|
||||||
|
if prop_distance < current_distance {
|
||||||
|
(prop_distance, prop_class)
|
||||||
|
} else {
|
||||||
|
(current_distance, current_class)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
minimal_class
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default)]
|
||||||
|
pub struct BuiltinElement {
|
||||||
|
pub native_class: Rc<NativeClass>,
|
||||||
|
pub properties: HashMap<String, Type>,
|
||||||
|
pub default_bindings: HashMap<String, Expression>,
|
||||||
|
pub additional_accepted_child_types: HashMap<String, Type>,
|
||||||
|
pub disallow_global_types_as_child_elements: bool,
|
||||||
|
/// Non-item type do not have reserved properties (x/width/rowspan/...) added to them (eg: PropertyAnimation)
|
||||||
|
pub is_non_item_type: bool,
|
||||||
|
pub member_functions: HashMap<String, Expression>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BuiltinElement {
|
||||||
|
pub fn new(native_class: Rc<NativeClass>) -> Self {
|
||||||
|
let mut properties = HashMap::new();
|
||||||
|
native_class.clone().visit_class_hierarchy(|class| {
|
||||||
|
for (prop_name, prop_type) in class.local_property_iter() {
|
||||||
|
properties.insert(prop_name.clone(), prop_type.clone());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Self { native_class, properties, ..Default::default() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_select_minimal_class_based_on_property_usage() {
|
||||||
|
let first = Rc::new(NativeClass::new_with_properties(
|
||||||
|
"first_class",
|
||||||
|
[("first_prop".to_owned(), Type::Int32)].iter().cloned(),
|
||||||
|
));
|
||||||
|
|
||||||
|
let mut second = NativeClass::new_with_properties(
|
||||||
|
"second_class",
|
||||||
|
[("second_prop".to_owned(), Type::Int32)].iter().cloned(),
|
||||||
|
);
|
||||||
|
second.parent = Some(first.clone());
|
||||||
|
let second = Rc::new(second);
|
||||||
|
|
||||||
|
let reduce_to_first = second
|
||||||
|
.clone()
|
||||||
|
.select_minimal_class_based_on_property_usage(["first_prop".to_owned()].iter());
|
||||||
|
|
||||||
|
assert_eq!(reduce_to_first.class_name, first.class_name);
|
||||||
|
|
||||||
|
let reduce_to_second = second
|
||||||
|
.clone()
|
||||||
|
.select_minimal_class_based_on_property_usage(["second_prop".to_owned()].iter());
|
||||||
|
|
||||||
|
assert_eq!(reduce_to_second.class_name, second.class_name);
|
||||||
|
|
||||||
|
let reduce_to_second = second.clone().select_minimal_class_based_on_property_usage(
|
||||||
|
["first_prop".to_owned(), "second_prop".to_owned()].iter(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(reduce_to_second.class_name, second.class_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Enumeration {
|
||||||
|
pub name: String,
|
||||||
|
pub values: Vec<String>,
|
||||||
|
pub default_value: usize, // index in values
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Enumeration {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.name.eq(&other.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Enumeration {
|
||||||
|
pub fn default_value(self: Rc<Self>) -> EnumerationValue {
|
||||||
|
EnumerationValue { value: self.default_value, enumeration: self.clone() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn try_value_from_string(self: Rc<Self>, value: &str) -> Option<EnumerationValue> {
|
||||||
|
self.values.iter().enumerate().find_map(|(idx, name)| {
|
||||||
|
if name == value {
|
||||||
|
Some(EnumerationValue { value: idx, enumeration: self.clone() })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct EnumerationValue {
|
||||||
|
pub value: usize, // index in enumeration.values
|
||||||
|
pub enumeration: Rc<Enumeration>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for EnumerationValue {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
Rc::ptr_eq(&self.enumeration, &other.enumeration) && self.value == other.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for EnumerationValue {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.enumeration.values[self.value].fmt(f)
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,8 +10,9 @@ LICENSE END */
|
||||||
//! Datastructures used to represent layouts in the compiler
|
//! Datastructures used to represent layouts in the compiler
|
||||||
|
|
||||||
use crate::expression_tree::{Expression, NamedReference, Path};
|
use crate::expression_tree::{Expression, NamedReference, Path};
|
||||||
|
use crate::langtype::Type;
|
||||||
use crate::object_tree::{ElementRc, PropertyDeclaration};
|
use crate::object_tree::{ElementRc, PropertyDeclaration};
|
||||||
use crate::{passes::ExpressionFieldsVisitor, typeregister::Type};
|
use crate::passes::ExpressionFieldsVisitor;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[derive(Debug, derive_more::From)]
|
#[derive(Debug, derive_more::From)]
|
||||||
|
|
|
@ -27,6 +27,7 @@ use std::{borrow::Cow, cell::RefCell, rc::Rc};
|
||||||
pub mod diagnostics;
|
pub mod diagnostics;
|
||||||
pub mod expression_tree;
|
pub mod expression_tree;
|
||||||
pub mod generator;
|
pub mod generator;
|
||||||
|
pub mod langtype;
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
pub mod object_tree;
|
pub mod object_tree;
|
||||||
|
|
|
@ -13,8 +13,9 @@ LICENSE END */
|
||||||
|
|
||||||
use crate::diagnostics::{FileDiagnostics, Spanned, SpannedWithSourceFile};
|
use crate::diagnostics::{FileDiagnostics, Spanned, SpannedWithSourceFile};
|
||||||
use crate::expression_tree::{Expression, ExpressionSpanned, NamedReference};
|
use crate::expression_tree::{Expression, ExpressionSpanned, NamedReference};
|
||||||
|
use crate::langtype::{NativeClass, Type};
|
||||||
use crate::parser::{identifier_text, syntax_nodes, SyntaxKind, SyntaxNodeWithSourceFile};
|
use crate::parser::{identifier_text, syntax_nodes, SyntaxKind, SyntaxNodeWithSourceFile};
|
||||||
use crate::typeregister::{NativeClass, Type, TypeRegister};
|
use crate::typeregister::TypeRegister;
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
|
@ -160,7 +161,7 @@ pub struct Element {
|
||||||
/// The id are then re-assigned unique id in the assign_id pass
|
/// The id are then re-assigned unique id in the assign_id pass
|
||||||
pub id: String,
|
pub id: String,
|
||||||
//pub base: QualifiedTypeName,
|
//pub base: QualifiedTypeName,
|
||||||
pub base_type: crate::typeregister::Type,
|
pub base_type: crate::langtype::Type,
|
||||||
/// Currently contains also the signals. FIXME: should that be changed?
|
/// Currently contains also the signals. FIXME: should that be changed?
|
||||||
pub bindings: HashMap<String, ExpressionSpanned>,
|
pub bindings: HashMap<String, ExpressionSpanned>,
|
||||||
pub children: Vec<ElementRc>,
|
pub children: Vec<ElementRc>,
|
||||||
|
|
|
@ -16,8 +16,8 @@ use crate::diagnostics::BuildDiagnostics;
|
||||||
/// elements property of the Path element. That way the generators have to deal
|
/// elements property of the Path element. That way the generators have to deal
|
||||||
/// with path embedding only as part of the property assignment.
|
/// with path embedding only as part of the property assignment.
|
||||||
use crate::expression_tree::*;
|
use crate::expression_tree::*;
|
||||||
|
use crate::langtype::Type;
|
||||||
use crate::object_tree::*;
|
use crate::object_tree::*;
|
||||||
use crate::typeregister::Type;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub fn compile_paths(
|
pub fn compile_paths(
|
||||||
|
@ -32,12 +32,12 @@ pub fn compile_paths(
|
||||||
|
|
||||||
recurse_elem(&component.root_element, &(), &mut |elem_, _| {
|
recurse_elem(&component.root_element, &(), &mut |elem_, _| {
|
||||||
let accepted_type = match &elem_.borrow().base_type {
|
let accepted_type = match &elem_.borrow().base_type {
|
||||||
crate::typeregister::Type::Builtin(be)
|
Type::Builtin(be)
|
||||||
if be.native_class.class_name == path_type.native_class.class_name =>
|
if be.native_class.class_name == path_type.native_class.class_name =>
|
||||||
{
|
{
|
||||||
path_type
|
path_type
|
||||||
}
|
}
|
||||||
crate::typeregister::Type::Builtin(be)
|
Type::Builtin(be)
|
||||||
if be.native_class.class_name == pathlayout_type.native_class.class_name =>
|
if be.native_class.class_name == pathlayout_type.native_class.class_name =>
|
||||||
{
|
{
|
||||||
pathlayout_type
|
pathlayout_type
|
||||||
|
|
|
@ -10,8 +10,8 @@ LICENSE END */
|
||||||
//! Do not read twice the same property, store in a local variable instead
|
//! Do not read twice the same property, store in a local variable instead
|
||||||
|
|
||||||
use crate::expression_tree::*;
|
use crate::expression_tree::*;
|
||||||
|
use crate::langtype::Type;
|
||||||
use crate::object_tree::*;
|
use crate::object_tree::*;
|
||||||
use crate::typeregister::Type;
|
|
||||||
use std::{cell::RefCell, collections::HashMap};
|
use std::{cell::RefCell, collections::HashMap};
|
||||||
|
|
||||||
pub fn deduplicate_property_read(component: &Component) {
|
pub fn deduplicate_property_read(component: &Component) {
|
||||||
|
|
|
@ -9,11 +9,9 @@
|
||||||
LICENSE END */
|
LICENSE END */
|
||||||
//! Inline each object_tree::Component within the main Component
|
//! Inline each object_tree::Component within the main Component
|
||||||
|
|
||||||
use crate::{
|
use crate::expression_tree::{Expression, NamedReference};
|
||||||
expression_tree::{Expression, NamedReference},
|
use crate::langtype::Type;
|
||||||
object_tree::*,
|
use crate::object_tree::*;
|
||||||
typeregister::Type,
|
|
||||||
};
|
|
||||||
use by_address::ByAddress;
|
use by_address::ByAddress;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
|
@ -11,8 +11,9 @@ LICENSE END */
|
||||||
|
|
||||||
use crate::diagnostics::BuildDiagnostics;
|
use crate::diagnostics::BuildDiagnostics;
|
||||||
use crate::expression_tree::*;
|
use crate::expression_tree::*;
|
||||||
|
use crate::langtype::Type;
|
||||||
use crate::layout::*;
|
use crate::layout::*;
|
||||||
use crate::{object_tree::*, typeregister::Type};
|
use crate::object_tree::*;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
fn property_reference(element: &ElementRc, name: &str) -> Box<Expression> {
|
fn property_reference(element: &ElementRc, name: &str) -> Box<Expression> {
|
||||||
|
|
|
@ -11,8 +11,8 @@ LICENSE END */
|
||||||
|
|
||||||
use crate::diagnostics::BuildDiagnostics;
|
use crate::diagnostics::BuildDiagnostics;
|
||||||
use crate::expression_tree::*;
|
use crate::expression_tree::*;
|
||||||
|
use crate::langtype::Type;
|
||||||
use crate::object_tree::*;
|
use crate::object_tree::*;
|
||||||
use crate::typeregister::Type;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
pub fn lower_states(component: &Rc<Component>, diag: &mut BuildDiagnostics) {
|
pub fn lower_states(component: &Rc<Component>, diag: &mut BuildDiagnostics) {
|
||||||
|
|
|
@ -10,8 +10,8 @@ LICENSE END */
|
||||||
//! This pass creates properties that are used but are otherwise not real
|
//! This pass creates properties that are used but are otherwise not real
|
||||||
|
|
||||||
use crate::expression_tree::NamedReference;
|
use crate::expression_tree::NamedReference;
|
||||||
|
use crate::langtype::Type;
|
||||||
use crate::object_tree::*;
|
use crate::object_tree::*;
|
||||||
use crate::typeregister::Type;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
@ -46,11 +46,9 @@ fn maybe_materialize(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let has_declared_property = match &base_type {
|
let has_declared_property = match &base_type {
|
||||||
crate::typeregister::Type::Component(c) => {
|
Type::Component(c) => has_declared_property(&c.root_element.borrow(), prop),
|
||||||
has_declared_property(&c.root_element.borrow(), prop)
|
Type::Builtin(b) => b.properties.contains_key(prop),
|
||||||
}
|
Type::Native(n) => n.lookup_property(prop).is_some(),
|
||||||
crate::typeregister::Type::Builtin(b) => b.properties.contains_key(prop),
|
|
||||||
crate::typeregister::Type::Native(n) => n.lookup_property(prop).is_some(),
|
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -72,11 +70,9 @@ fn has_declared_property(elem: &Element, prop: &str) -> bool {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
match &elem.base_type {
|
match &elem.base_type {
|
||||||
crate::typeregister::Type::Component(c) => {
|
Type::Component(c) => has_declared_property(&c.root_element.borrow(), prop),
|
||||||
has_declared_property(&c.root_element.borrow(), prop)
|
Type::Builtin(b) => b.properties.contains_key(prop),
|
||||||
}
|
Type::Native(n) => n.lookup_property(prop).is_some(),
|
||||||
crate::typeregister::Type::Builtin(b) => b.properties.contains_key(prop),
|
|
||||||
crate::typeregister::Type::Native(n) => n.lookup_property(prop).is_some(),
|
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,13 +9,12 @@
|
||||||
LICENSE END */
|
LICENSE END */
|
||||||
//! This pass moves all declaration of properties or signal to the root
|
//! This pass moves all declaration of properties or signal to the root
|
||||||
|
|
||||||
use crate::{
|
use crate::diagnostics::{BuildDiagnostics, Level};
|
||||||
diagnostics::{BuildDiagnostics, Level},
|
use crate::expression_tree::{Expression, NamedReference};
|
||||||
expression_tree::{Expression, NamedReference},
|
use crate::langtype::Type;
|
||||||
object_tree::*,
|
use crate::object_tree::*;
|
||||||
passes::ExpressionFieldsVisitor,
|
use crate::passes::ExpressionFieldsVisitor;
|
||||||
typeregister::Type,
|
|
||||||
};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,8 @@ Make sure that the Repeated expression are just components without any chilodren
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::expression_tree::NamedReference;
|
use crate::expression_tree::NamedReference;
|
||||||
|
use crate::langtype::Type;
|
||||||
use crate::object_tree::*;
|
use crate::object_tree::*;
|
||||||
use crate::typeregister::Type;
|
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
pub fn process_repeater_components(component: &Rc<Component>) {
|
pub fn process_repeater_components(component: &Rc<Component>) {
|
||||||
|
|
|
@ -10,8 +10,8 @@ LICENSE END */
|
||||||
//! After inlining and moving declarations, all Element::base_type should be Type::BuiltinElement. This pass resolves them
|
//! After inlining and moving declarations, all Element::base_type should be Type::BuiltinElement. This pass resolves them
|
||||||
// to NativeClass and picking a variant that only contains the used properties.
|
// to NativeClass and picking a variant that only contains the used properties.
|
||||||
|
|
||||||
|
use crate::langtype::Type;
|
||||||
use crate::object_tree::{recurse_elem, Component};
|
use crate::object_tree::{recurse_elem, Component};
|
||||||
use crate::typeregister::Type;
|
|
||||||
|
|
||||||
pub fn resolve_native_classes(component: &Component) {
|
pub fn resolve_native_classes(component: &Component) {
|
||||||
recurse_elem(&component.root_element, &(), &mut |elem, _| {
|
recurse_elem(&component.root_element, &(), &mut |elem, _| {
|
||||||
|
|
|
@ -16,9 +16,9 @@ LICENSE END */
|
||||||
|
|
||||||
use crate::diagnostics::BuildDiagnostics;
|
use crate::diagnostics::BuildDiagnostics;
|
||||||
use crate::expression_tree::*;
|
use crate::expression_tree::*;
|
||||||
|
use crate::langtype::Type;
|
||||||
use crate::object_tree::*;
|
use crate::object_tree::*;
|
||||||
use crate::parser::{identifier_text, syntax_nodes, SyntaxKind, SyntaxNodeWithSourceFile};
|
use crate::parser::{identifier_text, syntax_nodes, SyntaxKind, SyntaxNodeWithSourceFile};
|
||||||
use crate::typeregister::Type;
|
|
||||||
use by_address::ByAddress;
|
use by_address::ByAddress;
|
||||||
use std::{collections::HashMap, collections::HashSet, rc::Rc};
|
use std::{collections::HashMap, collections::HashSet, rc::Rc};
|
||||||
|
|
||||||
|
|
|
@ -7,495 +7,13 @@
|
||||||
This file is also available under commercial licensing terms.
|
This file is also available under commercial licensing terms.
|
||||||
Please contact info@sixtyfps.io for more information.
|
Please contact info@sixtyfps.io for more information.
|
||||||
LICENSE END */
|
LICENSE END */
|
||||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::{cell::RefCell, fmt::Display, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
use crate::expression_tree::{BuiltinFunction, Expression, Unit};
|
use crate::expression_tree::{BuiltinFunction, Expression, Unit};
|
||||||
|
use crate::langtype::{BuiltinElement, Enumeration, NativeClass, Type};
|
||||||
use crate::object_tree::{Component, Element};
|
use crate::object_tree::{Component, Element};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum Type {
|
|
||||||
/// Correspond to an uninitialized type, or an error
|
|
||||||
Invalid,
|
|
||||||
/// The type of an expression that return nothing
|
|
||||||
Void,
|
|
||||||
Component(Rc<crate::object_tree::Component>),
|
|
||||||
Builtin(Rc<BuiltinElement>),
|
|
||||||
Native(Rc<NativeClass>),
|
|
||||||
|
|
||||||
Signal {
|
|
||||||
args: Vec<Type>,
|
|
||||||
},
|
|
||||||
Function {
|
|
||||||
return_type: Box<Type>,
|
|
||||||
args: Vec<Type>,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Other property types:
|
|
||||||
Float32,
|
|
||||||
Int32,
|
|
||||||
String,
|
|
||||||
Color,
|
|
||||||
Duration,
|
|
||||||
Length,
|
|
||||||
LogicalLength,
|
|
||||||
Percent,
|
|
||||||
Resource,
|
|
||||||
Bool,
|
|
||||||
Model,
|
|
||||||
PathElements,
|
|
||||||
Easing,
|
|
||||||
|
|
||||||
Array(Box<Type>),
|
|
||||||
Object(BTreeMap<String, Type>),
|
|
||||||
|
|
||||||
Enumeration(Rc<Enumeration>),
|
|
||||||
EnumerationValue(EnumerationValue),
|
|
||||||
|
|
||||||
ElementReference,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl core::cmp::PartialEq for Type {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
match self {
|
|
||||||
Type::Invalid => matches!(other, Type::Invalid),
|
|
||||||
Type::Void => matches!(other, Type::Void),
|
|
||||||
Type::Component(a) => matches!(other, Type::Component(b) if Rc::ptr_eq(a, b)),
|
|
||||||
Type::Builtin(a) => matches!(other, Type::Builtin(b) if Rc::ptr_eq(a, b)),
|
|
||||||
Type::Native(a) => matches!(other, Type::Native(b) if Rc::ptr_eq(a, b)),
|
|
||||||
Type::Signal { args: a } => matches!(other, Type::Signal { args: b } if a == b),
|
|
||||||
Type::Function { return_type: lhs_rt, args: lhs_args } => {
|
|
||||||
matches!(other, Type::Function { return_type: rhs_rt, args: rhs_args } if lhs_rt == rhs_rt && lhs_args == rhs_args)
|
|
||||||
}
|
|
||||||
Type::Float32 => matches!(other, Type::Float32),
|
|
||||||
Type::Int32 => matches!(other, Type::Int32),
|
|
||||||
Type::String => matches!(other, Type::String),
|
|
||||||
Type::Color => matches!(other, Type::Color),
|
|
||||||
Type::Duration => matches!(other, Type::Duration),
|
|
||||||
Type::Length => matches!(other, Type::Length),
|
|
||||||
Type::LogicalLength => matches!(other, Type::LogicalLength),
|
|
||||||
Type::Percent => matches!(other, Type::Percent),
|
|
||||||
Type::Resource => matches!(other, Type::Resource),
|
|
||||||
Type::Bool => matches!(other, Type::Bool),
|
|
||||||
Type::Model => matches!(other, Type::Model),
|
|
||||||
Type::PathElements => matches!(other, Type::PathElements),
|
|
||||||
Type::Easing => matches!(other, Type::Easing),
|
|
||||||
Type::Array(a) => matches!(other, Type::Array(b) if a == b),
|
|
||||||
Type::Object(a) => matches!(other, Type::Object(b) if a == b),
|
|
||||||
Type::Enumeration(lhs) => matches!(other, Type::Enumeration(rhs) if lhs == rhs),
|
|
||||||
Type::EnumerationValue(lhs) => {
|
|
||||||
matches!(other, Type::EnumerationValue(rhs) if lhs == rhs)
|
|
||||||
}
|
|
||||||
Type::ElementReference => matches!(other, Type::ElementReference),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Type {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
Type::Invalid => write!(f, "<error>"),
|
|
||||||
Type::Void => write!(f, "void"),
|
|
||||||
Type::Component(c) => c.id.fmt(f),
|
|
||||||
Type::Builtin(b) => b.native_class.class_name.fmt(f),
|
|
||||||
Type::Native(b) => b.class_name.fmt(f),
|
|
||||||
Type::Signal { args } => {
|
|
||||||
write!(f, "signal")?;
|
|
||||||
if !args.is_empty() {
|
|
||||||
write!(f, "(")?;
|
|
||||||
for (i, arg) in args.iter().enumerate() {
|
|
||||||
if i > 0 {
|
|
||||||
write!(f, ",")?;
|
|
||||||
}
|
|
||||||
write!(f, "{}", arg)?;
|
|
||||||
}
|
|
||||||
write!(f, ")")?
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Type::Function { return_type, args } => {
|
|
||||||
write!(f, "function(")?;
|
|
||||||
for (i, arg) in args.iter().enumerate() {
|
|
||||||
if i > 0 {
|
|
||||||
write!(f, ",")?;
|
|
||||||
}
|
|
||||||
write!(f, "{}", arg)?;
|
|
||||||
}
|
|
||||||
write!(f, ") -> {}", return_type)
|
|
||||||
}
|
|
||||||
Type::Float32 => write!(f, "float"),
|
|
||||||
Type::Int32 => write!(f, "int"),
|
|
||||||
Type::String => write!(f, "string"),
|
|
||||||
Type::Duration => write!(f, "duration"),
|
|
||||||
Type::Length => write!(f, "length"),
|
|
||||||
Type::LogicalLength => write!(f, "logical_length"),
|
|
||||||
Type::Percent => write!(f, "percent"),
|
|
||||||
Type::Color => write!(f, "color"),
|
|
||||||
Type::Resource => write!(f, "resource"),
|
|
||||||
Type::Bool => write!(f, "bool"),
|
|
||||||
Type::Model => write!(f, "model"),
|
|
||||||
Type::Array(t) => write!(f, "[{}]", t),
|
|
||||||
Type::Object(t) => {
|
|
||||||
write!(f, "{{ ")?;
|
|
||||||
for (k, v) in t {
|
|
||||||
write!(f, "{}: {},", k, v)?;
|
|
||||||
}
|
|
||||||
write!(f, "}}")
|
|
||||||
}
|
|
||||||
Type::PathElements => write!(f, "pathelements"),
|
|
||||||
Type::Easing => write!(f, "easing"),
|
|
||||||
Type::Enumeration(enumeration) => write!(f, "enum {}", enumeration.name),
|
|
||||||
Type::EnumerationValue(value) => {
|
|
||||||
write!(f, "enum {}::{}", value.enumeration.name, value.to_string())
|
|
||||||
}
|
|
||||||
Type::ElementReference => write!(f, "element ref"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Type {
|
|
||||||
pub fn is_object_type(&self) -> bool {
|
|
||||||
matches!(self, Self::Component(_) | Self::Builtin(_))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// valid type for properties
|
|
||||||
pub fn is_property_type(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
Self::Float32
|
|
||||||
| Self::Int32
|
|
||||||
| Self::String
|
|
||||||
| Self::Color
|
|
||||||
| Self::Duration
|
|
||||||
| Self::Length
|
|
||||||
| Self::LogicalLength
|
|
||||||
| Self::Percent
|
|
||||||
| Self::Resource
|
|
||||||
| Self::Bool
|
|
||||||
| Self::Model
|
|
||||||
| Self::Easing
|
|
||||||
| Self::Enumeration(_)
|
|
||||||
| Self::ElementReference
|
|
||||||
| Self::Object(_)
|
|
||||||
| Self::Array(_) => true,
|
|
||||||
Self::Component(c) => c.root_element.borrow().base_type == Type::Void,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ok_for_public_api(&self) -> bool {
|
|
||||||
// Duration and Easing don't have good types for public API exposure yet.
|
|
||||||
!matches!(self, Self::Duration | Self::Easing)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lookup_property(&self, name: &str) -> Type {
|
|
||||||
match self {
|
|
||||||
Type::Component(c) => c.root_element.borrow().lookup_property(name),
|
|
||||||
Type::Builtin(b) => b.properties.get(name).cloned().unwrap_or_else(|| {
|
|
||||||
if b.is_non_item_type {
|
|
||||||
Type::Invalid
|
|
||||||
} else {
|
|
||||||
reserved_property(name)
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
Type::Native(n) => n.lookup_property(name).unwrap_or_default(),
|
|
||||||
_ => Type::Invalid,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lookup_type_for_child_element(
|
|
||||||
&self,
|
|
||||||
name: &str,
|
|
||||||
tr: &TypeRegister,
|
|
||||||
) -> Result<Type, String> {
|
|
||||||
match self {
|
|
||||||
Type::Component(component) => {
|
|
||||||
return component
|
|
||||||
.root_element
|
|
||||||
.borrow()
|
|
||||||
.base_type
|
|
||||||
.lookup_type_for_child_element(name, tr)
|
|
||||||
}
|
|
||||||
Type::Builtin(builtin) => {
|
|
||||||
if let Some(child_type) = builtin.additional_accepted_child_types.get(name) {
|
|
||||||
return Ok(child_type.clone());
|
|
||||||
}
|
|
||||||
if builtin.disallow_global_types_as_child_elements {
|
|
||||||
let mut valid_children: Vec<_> =
|
|
||||||
builtin.additional_accepted_child_types.keys().cloned().collect();
|
|
||||||
valid_children.sort();
|
|
||||||
|
|
||||||
return Err(format!(
|
|
||||||
"{} is not allowed within {}. Only {} are valid children",
|
|
||||||
name,
|
|
||||||
builtin.native_class.class_name,
|
|
||||||
valid_children.join(" ")
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
tr.lookup_element(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lookup_member_function(&self, name: &str) -> Expression {
|
|
||||||
match self {
|
|
||||||
Type::Builtin(builtin) => builtin.member_functions.get(name).unwrap().clone(),
|
|
||||||
_ => Expression::Invalid,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Assume this is a builtin type, panic if it isn't
|
|
||||||
pub fn as_builtin(&self) -> &BuiltinElement {
|
|
||||||
match self {
|
|
||||||
Type::Builtin(b) => &b,
|
|
||||||
Type::Component(_) => panic!("This should not happen because of inlining"),
|
|
||||||
_ => panic!("invalid type"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Assume this is a builtin type, panic if it isn't
|
|
||||||
pub fn as_native(&self) -> &NativeClass {
|
|
||||||
match self {
|
|
||||||
Type::Native(b) => &b,
|
|
||||||
Type::Component(_) => {
|
|
||||||
panic!("This should not happen because of native class resolution")
|
|
||||||
}
|
|
||||||
_ => panic!("invalid type"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Assime it is a Component, panic if it isn't
|
|
||||||
pub fn as_component(&self) -> &Rc<crate::object_tree::Component> {
|
|
||||||
match self {
|
|
||||||
Type::Component(c) => c,
|
|
||||||
_ => panic!("should be a component because of the repeater_component pass"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return true if the type can be converted to the other type
|
|
||||||
pub fn can_convert(&self, other: &Self) -> bool {
|
|
||||||
let can_convert_object = |a: &BTreeMap<String, Type>, b: &BTreeMap<String, Type>| {
|
|
||||||
// the object `b` has property that the object `a` doesn't
|
|
||||||
let mut has_more_property = false;
|
|
||||||
for (k, v) in b {
|
|
||||||
match a.get(k) {
|
|
||||||
Some(t) if !t.can_convert(v) => return false,
|
|
||||||
None => has_more_property = true,
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if has_more_property {
|
|
||||||
// we should reject the conversion if `a` has property that `b` doesn't have
|
|
||||||
if a.keys().any(|k| !b.contains_key(k)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
};
|
|
||||||
let can_convert_object_to_component = |a: &BTreeMap<String, Type>, c: &Component| {
|
|
||||||
let root_element = c.root_element.borrow();
|
|
||||||
if root_element.base_type != Type::Void {
|
|
||||||
//component is not a struct
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (k, v) in &root_element.property_declarations {
|
|
||||||
if !a.get(k).map_or(false, |t| t.can_convert(&v.property_type)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
};
|
|
||||||
|
|
||||||
match (self, other) {
|
|
||||||
(a, b) if a == b => true,
|
|
||||||
(_, Type::Invalid)
|
|
||||||
| (_, Type::Void)
|
|
||||||
| (Type::Float32, Type::Int32)
|
|
||||||
| (Type::Float32, Type::String)
|
|
||||||
| (Type::Int32, Type::Float32)
|
|
||||||
| (Type::Int32, Type::String)
|
|
||||||
| (Type::Array(_), Type::Model)
|
|
||||||
| (Type::Float32, Type::Model)
|
|
||||||
| (Type::Int32, Type::Model)
|
|
||||||
| (Type::Length, Type::LogicalLength)
|
|
||||||
| (Type::LogicalLength, Type::Length)
|
|
||||||
| (Type::Percent, Type::Float32) => true,
|
|
||||||
(Type::Object(a), Type::Object(b)) if can_convert_object(a, b) => true,
|
|
||||||
(Type::Object(a), Type::Component(c)) if can_convert_object_to_component(a, c) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn collect_contextual_types(
|
|
||||||
&self,
|
|
||||||
context_restricted_types: &mut HashMap<String, HashSet<String>>,
|
|
||||||
) {
|
|
||||||
let builtin = match self {
|
|
||||||
Type::Builtin(ty) => ty,
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
for (accepted_child_type_name, accepted_child_type) in
|
|
||||||
builtin.additional_accepted_child_types.iter()
|
|
||||||
{
|
|
||||||
context_restricted_types
|
|
||||||
.entry(accepted_child_type_name.clone())
|
|
||||||
.or_default()
|
|
||||||
.insert(builtin.native_class.class_name.clone());
|
|
||||||
|
|
||||||
accepted_child_type.collect_contextual_types(context_restricted_types);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If this is a number type which should be used with an unit, this returns the default unit
|
|
||||||
/// otherwise, returns None
|
|
||||||
pub fn default_unit(&self) -> Option<Unit> {
|
|
||||||
match self {
|
|
||||||
Type::Duration => Some(Unit::Ms),
|
|
||||||
Type::Length => Some(Unit::Phx),
|
|
||||||
Type::LogicalLength => Some(Unit::Px),
|
|
||||||
Type::Percent => Some(Unit::Percent),
|
|
||||||
Type::Invalid => None,
|
|
||||||
Type::Void => None,
|
|
||||||
Type::Component(_) => None,
|
|
||||||
Type::Builtin(_) => None,
|
|
||||||
Type::Native(_) => None,
|
|
||||||
Type::Signal { .. } => None,
|
|
||||||
Type::Function { .. } => None,
|
|
||||||
Type::Float32 => None,
|
|
||||||
Type::Int32 => None,
|
|
||||||
Type::String => None,
|
|
||||||
Type::Color => None,
|
|
||||||
Type::Resource => None,
|
|
||||||
Type::Bool => None,
|
|
||||||
Type::Model => None,
|
|
||||||
Type::PathElements => None,
|
|
||||||
Type::Easing => None,
|
|
||||||
Type::Array(_) => None,
|
|
||||||
Type::Object(_) => None,
|
|
||||||
Type::Enumeration(_) => None,
|
|
||||||
Type::EnumerationValue(_) => None,
|
|
||||||
Type::ElementReference => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Type {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Invalid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
pub struct NativeClass {
|
|
||||||
pub parent: Option<Rc<NativeClass>>,
|
|
||||||
pub class_name: String,
|
|
||||||
pub vtable_symbol: String,
|
|
||||||
pub properties: HashMap<String, Type>,
|
|
||||||
pub cpp_type: Option<String>,
|
|
||||||
pub rust_type_constructor: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl NativeClass {
|
|
||||||
pub fn new(class_name: &str) -> Self {
|
|
||||||
let vtable_symbol = format!("{}VTable", class_name);
|
|
||||||
Self {
|
|
||||||
class_name: class_name.into(),
|
|
||||||
vtable_symbol,
|
|
||||||
properties: Default::default(),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn new_with_properties(
|
|
||||||
class_name: &str,
|
|
||||||
properties: impl IntoIterator<Item = (String, Type)>,
|
|
||||||
) -> Self {
|
|
||||||
let mut class = Self::new(class_name);
|
|
||||||
class.properties = properties.into_iter().collect();
|
|
||||||
class
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn property_count(&self) -> usize {
|
|
||||||
self.properties.len() + self.parent.clone().map(|p| p.property_count()).unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn local_property_iter(&self) -> impl Iterator<Item = (&String, &Type)> {
|
|
||||||
self.properties.iter()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn visit_class_hierarchy(self: Rc<Self>, mut visitor: impl FnMut(&Rc<Self>)) {
|
|
||||||
visitor(&self);
|
|
||||||
if let Some(parent_class) = &self.parent {
|
|
||||||
parent_class.clone().visit_class_hierarchy(visitor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lookup_property(&self, name: &str) -> Option<Type> {
|
|
||||||
if let Some(ty) = self.properties.get(name) {
|
|
||||||
Some(ty.clone())
|
|
||||||
} else if let Some(parent_class) = &self.parent {
|
|
||||||
parent_class.lookup_property(name)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lookup_property_distance(self: Rc<Self>, name: &str) -> (usize, Rc<Self>) {
|
|
||||||
let mut distance = 0;
|
|
||||||
let mut class = self.clone();
|
|
||||||
loop {
|
|
||||||
if class.properties.contains_key(name) {
|
|
||||||
return (distance, class);
|
|
||||||
}
|
|
||||||
distance += 1;
|
|
||||||
class = class.parent.as_ref().unwrap().clone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn select_minimal_class_based_on_property_usage<'a>(
|
|
||||||
self: Rc<Self>,
|
|
||||||
properties_used: impl Iterator<Item = &'a String>,
|
|
||||||
) -> Rc<Self> {
|
|
||||||
let (_min_distance, minimal_class) = properties_used.fold(
|
|
||||||
(std::usize::MAX, self.clone()),
|
|
||||||
|(current_distance, current_class), prop_name| {
|
|
||||||
let (prop_distance, prop_class) = self.clone().lookup_property_distance(&prop_name);
|
|
||||||
|
|
||||||
if prop_distance < current_distance {
|
|
||||||
(prop_distance, prop_class)
|
|
||||||
} else {
|
|
||||||
(current_distance, current_class)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
minimal_class
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
pub struct BuiltinElement {
|
|
||||||
pub native_class: Rc<NativeClass>,
|
|
||||||
pub properties: HashMap<String, Type>,
|
|
||||||
pub default_bindings: HashMap<String, Expression>,
|
|
||||||
pub additional_accepted_child_types: HashMap<String, Type>,
|
|
||||||
pub disallow_global_types_as_child_elements: bool,
|
|
||||||
/// Non-item type do not have reserved properties (x/width/rowspan/...) added to them (eg: PropertyAnimation)
|
|
||||||
pub is_non_item_type: bool,
|
|
||||||
pub member_functions: HashMap<String, Expression>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BuiltinElement {
|
|
||||||
pub fn new(native_class: Rc<NativeClass>) -> Self {
|
|
||||||
let mut properties = HashMap::new();
|
|
||||||
native_class.clone().visit_class_hierarchy(|class| {
|
|
||||||
for (prop_name, prop_type) in class.local_property_iter() {
|
|
||||||
properties.insert(prop_name.clone(), prop_type.clone());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Self { native_class, properties, ..Default::default() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// reserved property injected in every item
|
/// reserved property injected in every item
|
||||||
pub fn reserved_property(name: &str) -> Type {
|
pub fn reserved_property(name: &str) -> Type {
|
||||||
for (p, t) in [
|
for (p, t) in [
|
||||||
|
@ -1069,83 +587,3 @@ impl TypeRegister {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_select_minimal_class_based_on_property_usage() {
|
|
||||||
let first = Rc::new(NativeClass::new_with_properties(
|
|
||||||
"first_class",
|
|
||||||
[("first_prop".to_owned(), Type::Int32)].iter().cloned(),
|
|
||||||
));
|
|
||||||
|
|
||||||
let mut second = NativeClass::new_with_properties(
|
|
||||||
"second_class",
|
|
||||||
[("second_prop".to_owned(), Type::Int32)].iter().cloned(),
|
|
||||||
);
|
|
||||||
second.parent = Some(first.clone());
|
|
||||||
let second = Rc::new(second);
|
|
||||||
|
|
||||||
let reduce_to_first = second
|
|
||||||
.clone()
|
|
||||||
.select_minimal_class_based_on_property_usage(["first_prop".to_owned()].iter());
|
|
||||||
|
|
||||||
assert_eq!(reduce_to_first.class_name, first.class_name);
|
|
||||||
|
|
||||||
let reduce_to_second = second
|
|
||||||
.clone()
|
|
||||||
.select_minimal_class_based_on_property_usage(["second_prop".to_owned()].iter());
|
|
||||||
|
|
||||||
assert_eq!(reduce_to_second.class_name, second.class_name);
|
|
||||||
|
|
||||||
let reduce_to_second = second.clone().select_minimal_class_based_on_property_usage(
|
|
||||||
["first_prop".to_owned(), "second_prop".to_owned()].iter(),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(reduce_to_second.class_name, second.class_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Enumeration {
|
|
||||||
pub name: String,
|
|
||||||
pub values: Vec<String>,
|
|
||||||
pub default_value: usize, // index in values
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for Enumeration {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.name.eq(&other.name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Enumeration {
|
|
||||||
pub fn default_value(self: Rc<Self>) -> EnumerationValue {
|
|
||||||
EnumerationValue { value: self.default_value, enumeration: self.clone() }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_value_from_string(self: Rc<Self>, value: &str) -> Option<EnumerationValue> {
|
|
||||||
self.values.iter().enumerate().find_map(|(idx, name)| {
|
|
||||||
if name == value {
|
|
||||||
Some(EnumerationValue { value: idx, enumeration: self.clone() })
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct EnumerationValue {
|
|
||||||
pub value: usize, // index in enumeration.values
|
|
||||||
pub enumeration: Rc<Enumeration>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for EnumerationValue {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
Rc::ptr_eq(&self.enumeration, &other.enumeration) && self.value == other.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for EnumerationValue {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
self.enumeration.values[self.value].fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -15,10 +15,10 @@ use dynamic_type::{Instance, InstanceBox};
|
||||||
use expression_tree::NamedReference;
|
use expression_tree::NamedReference;
|
||||||
use object_tree::{Element, ElementRc};
|
use object_tree::{Element, ElementRc};
|
||||||
use sixtyfps_compilerlib::expression_tree::Expression;
|
use sixtyfps_compilerlib::expression_tree::Expression;
|
||||||
|
use sixtyfps_compilerlib::langtype::Type;
|
||||||
use sixtyfps_compilerlib::layout::{
|
use sixtyfps_compilerlib::layout::{
|
||||||
GridLayout, Layout, LayoutConstraints, LayoutElement, LayoutItem, PathLayout,
|
GridLayout, Layout, LayoutConstraints, LayoutElement, LayoutItem, PathLayout,
|
||||||
};
|
};
|
||||||
use sixtyfps_compilerlib::typeregister::Type;
|
|
||||||
use sixtyfps_compilerlib::*;
|
use sixtyfps_compilerlib::*;
|
||||||
use sixtyfps_corelib::component::{Component, ComponentRefPin, ComponentVTable};
|
use sixtyfps_corelib::component::{Component, ComponentRefPin, ComponentVTable};
|
||||||
use sixtyfps_corelib::graphics::Resource;
|
use sixtyfps_corelib::graphics::Resource;
|
||||||
|
|
|
@ -15,7 +15,8 @@ use sixtyfps_compilerlib::expression_tree::{
|
||||||
BuiltinFunction, EasingCurve, Expression, ExpressionSpanned, NamedReference, Path as ExprPath,
|
BuiltinFunction, EasingCurve, Expression, ExpressionSpanned, NamedReference, Path as ExprPath,
|
||||||
PathElement as ExprPathElement,
|
PathElement as ExprPathElement,
|
||||||
};
|
};
|
||||||
use sixtyfps_compilerlib::{object_tree::ElementRc, typeregister::Type};
|
use sixtyfps_compilerlib::langtype::Type;
|
||||||
|
use sixtyfps_compilerlib::object_tree::ElementRc;
|
||||||
use sixtyfps_corelib as corelib;
|
use sixtyfps_corelib as corelib;
|
||||||
use sixtyfps_corelib::{
|
use sixtyfps_corelib::{
|
||||||
graphics::PathElement, items::ItemRef, items::PropertyAnimation, Color, PathData, Resource,
|
graphics::PathElement, items::ItemRef, items::PropertyAnimation, Color, PathData, Resource,
|
||||||
|
|
|
@ -34,7 +34,7 @@ impl<'id> dynamic_component::ComponentDescription<'id> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List of publicly declared properties or signal
|
/// List of publicly declared properties or signal
|
||||||
pub fn properties(&self) -> HashMap<String, sixtyfps_compilerlib::typeregister::Type> {
|
pub fn properties(&self) -> HashMap<String, sixtyfps_compilerlib::langtype::Type> {
|
||||||
self.original
|
self.original
|
||||||
.root_element
|
.root_element
|
||||||
.borrow()
|
.borrow()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue