mirror of
https://github.com/slint-ui/slint.git
synced 2025-09-29 13:24:48 +00:00
More progress in states and transition parsing
Fill the object_tree with states, and part of the transition Also make sure to duplicate animations properly in inlining
This commit is contained in:
parent
0dff3f5f78
commit
c0fab1c3e9
5 changed files with 238 additions and 47 deletions
|
@ -3,7 +3,7 @@
|
|||
*/
|
||||
|
||||
use crate::diagnostics::FileDiagnostics;
|
||||
use crate::expression_tree::Expression;
|
||||
use crate::expression_tree::{Expression, NamedReference};
|
||||
use crate::parser::{syntax_nodes, Spanned, SyntaxKind, SyntaxNodeEx};
|
||||
use crate::typeregister::{Type, TypeRegister};
|
||||
use std::cell::RefCell;
|
||||
|
@ -80,13 +80,13 @@ impl Component {
|
|||
) -> Rc<Self> {
|
||||
let c = Rc::new(Component {
|
||||
id: node.child_text(SyntaxKind::Identifier).unwrap_or_default(),
|
||||
root_element: Rc::new(RefCell::new(Element::from_node(
|
||||
root_element: Element::from_node(
|
||||
node.Element(),
|
||||
"root".into(),
|
||||
Type::Invalid,
|
||||
diag,
|
||||
tr,
|
||||
))),
|
||||
),
|
||||
..Default::default()
|
||||
});
|
||||
let weak = Rc::downgrade(&c);
|
||||
|
@ -128,6 +128,9 @@ pub struct Element {
|
|||
/// Tis element is part of a `for <xxx> in <model>:
|
||||
pub repeated: Option<RepeatedElementInfo>,
|
||||
|
||||
pub states: Vec<State>,
|
||||
pub transitions: Vec<Transition>,
|
||||
|
||||
/// The AST node, if available
|
||||
pub node: Option<syntax_nodes::Element>,
|
||||
}
|
||||
|
@ -153,7 +156,7 @@ impl Element {
|
|||
parent_type: Type,
|
||||
diag: &mut FileDiagnostics,
|
||||
tr: &TypeRegister,
|
||||
) -> Self {
|
||||
) -> ElementRc {
|
||||
let base = QualifiedTypeName::from_node(node.QualifiedName());
|
||||
let mut r = Element {
|
||||
id,
|
||||
|
@ -161,7 +164,7 @@ impl Element {
|
|||
Ok(ty) => ty,
|
||||
Err(err) => {
|
||||
diag.push_error(err, node.QualifiedName().span());
|
||||
return Element::default();
|
||||
return ElementRc::default();
|
||||
}
|
||||
},
|
||||
node: Some(node.clone()),
|
||||
|
@ -290,32 +293,80 @@ impl Element {
|
|||
if se.kind() == SyntaxKind::SubElement {
|
||||
let id = se.child_text(SyntaxKind::Identifier).unwrap_or_default();
|
||||
if let Some(element_node) = se.child_node(SyntaxKind::Element) {
|
||||
r.children.push(Rc::new(RefCell::new(Element::from_node(
|
||||
r.children.push(Element::from_node(
|
||||
element_node.into(),
|
||||
id,
|
||||
r.base_type.clone(),
|
||||
diag,
|
||||
tr,
|
||||
))));
|
||||
));
|
||||
} else {
|
||||
assert!(diag.has_error());
|
||||
}
|
||||
} else if se.kind() == SyntaxKind::RepeatedElement {
|
||||
r.children.push(Rc::new(RefCell::new(Element::from_repeated_node(
|
||||
r.children.push(Element::from_repeated_node(
|
||||
se.into(),
|
||||
r.base_type.clone(),
|
||||
diag,
|
||||
tr,
|
||||
))));
|
||||
));
|
||||
} else if se.kind() == SyntaxKind::ConditionalElement {
|
||||
r.children.push(Rc::new(RefCell::new(Element::from_conditional_node(
|
||||
r.children.push(Element::from_conditional_node(
|
||||
se.into(),
|
||||
r.base_type.clone(),
|
||||
diag,
|
||||
tr,
|
||||
))));
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let r = ElementRc::new(RefCell::new(r));
|
||||
|
||||
for state in node.States().flat_map(|s| s.State()) {
|
||||
let s = State {
|
||||
id: state
|
||||
.DeclaredIdentifier()
|
||||
.child_text(SyntaxKind::Identifier)
|
||||
.unwrap_or_default(),
|
||||
condition: state.Expression().map(|e| Expression::Uncompiled(e.into())),
|
||||
property_changes: state
|
||||
.StatePropertyChange()
|
||||
.map(|s| {
|
||||
let ne = lookup_property_from_qualified_name(s.QualifiedName(), &r, diag);
|
||||
(ne, Expression::Uncompiled(s.BindingExpression().into()))
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
r.borrow_mut().states.push(s);
|
||||
}
|
||||
|
||||
for trs in node.Transitions().flat_map(|s| s.Transition()) {
|
||||
let trans = Transition {
|
||||
is_out: trs.child_text(SyntaxKind::Identifier).unwrap_or_default() == "out",
|
||||
state_id: trs
|
||||
.DeclaredIdentifier()
|
||||
.child_text(SyntaxKind::Identifier)
|
||||
.unwrap_or_default(),
|
||||
property_animations: trs
|
||||
.PropertyAnimation()
|
||||
.map(|pa| {
|
||||
// TODO: do that properly
|
||||
(
|
||||
NamedReference {
|
||||
element: Rc::downgrade(&r),
|
||||
name: pa
|
||||
.DeclaredIdentifier()
|
||||
.child_text(SyntaxKind::Identifier)
|
||||
.unwrap_or_default(),
|
||||
},
|
||||
Default::default(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
};
|
||||
r.borrow_mut().transitions.push(trans);
|
||||
}
|
||||
|
||||
r
|
||||
}
|
||||
|
||||
|
@ -324,7 +375,7 @@ impl Element {
|
|||
parent_type: Type,
|
||||
diag: &mut FileDiagnostics,
|
||||
tr: &TypeRegister,
|
||||
) -> Self {
|
||||
) -> ElementRc {
|
||||
let rei = RepeatedElementInfo {
|
||||
model: Expression::Uncompiled(node.Expression().into()),
|
||||
model_data_id: node
|
||||
|
@ -337,8 +388,8 @@ impl Element {
|
|||
.unwrap_or_default(),
|
||||
is_conditional_element: false,
|
||||
};
|
||||
let mut e = Element::from_node(node.Element(), String::new(), parent_type, diag, tr);
|
||||
e.repeated = Some(rei);
|
||||
let e = Element::from_node(node.Element(), String::new(), parent_type, diag, tr);
|
||||
e.borrow_mut().repeated = Some(rei);
|
||||
e
|
||||
}
|
||||
|
||||
|
@ -347,15 +398,15 @@ impl Element {
|
|||
parent_type: Type,
|
||||
diag: &mut FileDiagnostics,
|
||||
tr: &TypeRegister,
|
||||
) -> Self {
|
||||
) -> ElementRc {
|
||||
let rei = RepeatedElementInfo {
|
||||
model: Expression::Uncompiled(node.Expression().into()),
|
||||
model_data_id: String::new(),
|
||||
index_id: String::new(),
|
||||
is_conditional_element: true,
|
||||
};
|
||||
let mut e = Element::from_node(node.Element(), String::new(), parent_type, diag, tr);
|
||||
e.repeated = Some(rei);
|
||||
let e = Element::from_node(node.Element(), String::new(), parent_type, diag, tr);
|
||||
e.borrow_mut().repeated = Some(rei);
|
||||
e
|
||||
}
|
||||
|
||||
|
@ -432,6 +483,59 @@ impl std::fmt::Display for QualifiedTypeName {
|
|||
}
|
||||
}
|
||||
|
||||
/// Return a NamedReference, if the reference is invalid, there will be a diagnostic
|
||||
fn lookup_property_from_qualified_name(
|
||||
node: syntax_nodes::QualifiedName,
|
||||
r: &Rc<RefCell<Element>>,
|
||||
diag: &mut FileDiagnostics,
|
||||
) -> NamedReference {
|
||||
let qualname = QualifiedTypeName::from_node(node.clone());
|
||||
match qualname.members.as_slice() {
|
||||
[prop_name] => {
|
||||
if !r.borrow().lookup_property(prop_name.as_ref()).is_property_type() {
|
||||
diag.push_error(format!("'{}' is not a valid property", qualname), node.span());
|
||||
}
|
||||
NamedReference { element: Rc::downgrade(&r), name: String::default() }
|
||||
}
|
||||
[elem_id, prop_name] => {
|
||||
let element = if let Some(element) = find_element_by_id(&r, elem_id.as_ref()) {
|
||||
if !element.borrow().lookup_property(prop_name.as_ref()).is_property_type() {
|
||||
diag.push_error(
|
||||
format!("'{}' not found in '{}'", prop_name, elem_id),
|
||||
node.span(),
|
||||
);
|
||||
}
|
||||
Rc::downgrade(&element)
|
||||
} else {
|
||||
diag.push_error(format!("'{}' is not a valid element id", elem_id), node.span());
|
||||
Weak::new()
|
||||
};
|
||||
NamedReference { element, name: prop_name.clone() }
|
||||
}
|
||||
_ => {
|
||||
diag.push_error(format!("'{}' is not a valid property", qualname), node.span());
|
||||
NamedReference { element: Default::default(), name: String::default() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// FIXME: this is duplicated the resolving pass. Also, we should use a hash table
|
||||
fn find_element_by_id(e: &ElementRc, name: &str) -> Option<ElementRc> {
|
||||
if e.borrow().id == name {
|
||||
return Some(e.clone());
|
||||
}
|
||||
for x in &e.borrow().children {
|
||||
if x.borrow().repeated.is_some() {
|
||||
continue;
|
||||
}
|
||||
if let Some(x) = find_element_by_id(x, name) {
|
||||
return Some(x);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Call the visitor for each children of the element recursively, starting with the element itself
|
||||
///
|
||||
/// The state returned by the visitor is passed to the children
|
||||
|
@ -445,3 +549,18 @@ pub fn recurse_elem<State>(
|
|||
recurse_elem(sub, &state, vis);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct State {
|
||||
pub id: String,
|
||||
pub condition: Option<Expression>,
|
||||
pub property_changes: Vec<(NamedReference, Expression)>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Transition {
|
||||
/// false for 'to', true for 'out'
|
||||
pub is_out: bool,
|
||||
pub state_id: String,
|
||||
pub property_animations: Vec<(NamedReference, ElementRc)>,
|
||||
}
|
||||
|
|
|
@ -80,20 +80,35 @@ fn inline_element(
|
|||
.borrow()
|
||||
.bindings
|
||||
.iter()
|
||||
.map(|(k, val)| (k.clone(), fold_binding(val, &mapping, root_component))),
|
||||
.map(|(k, val)| (k.clone(), fold_binding(val, &mapping))),
|
||||
);
|
||||
|
||||
//core::mem::drop(elem_mut);
|
||||
|
||||
// Now fixup all binding and reference
|
||||
for (key, e) in &mapping {
|
||||
if *key == element_key(&inlined_component.root_element) {
|
||||
continue; // the root has been processed
|
||||
}
|
||||
for (_, expr) in &mut e.borrow_mut().bindings {
|
||||
fixup_binding(expr, &mapping, root_component);
|
||||
fixup_binding(expr, &mapping);
|
||||
}
|
||||
if let Some(ref mut r) = &mut e.borrow_mut().repeated {
|
||||
fixup_binding(&mut r.model, &mapping, root_component);
|
||||
fixup_binding(&mut r.model, &mapping);
|
||||
}
|
||||
for s in &mut e.borrow_mut().states {
|
||||
if let Some(cond) = s.condition.as_mut() {
|
||||
fixup_binding(cond, &mapping)
|
||||
}
|
||||
for (r, e) in &mut s.property_changes {
|
||||
fixup_reference(r, &mapping);
|
||||
fixup_binding(e, &mapping);
|
||||
}
|
||||
}
|
||||
for t in &mut e.borrow_mut().transitions {
|
||||
for (r, _) in &mut t.property_animations {
|
||||
fixup_reference(r, &mapping)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -109,8 +124,12 @@ fn duplicate_element_with_mapping(
|
|||
base_type: elem.base_type.clone(),
|
||||
id: elem.id.clone(),
|
||||
property_declarations: elem.property_declarations.clone(),
|
||||
property_animations: elem.property_animations.clone(),
|
||||
// We will do the mapping of the binding later
|
||||
property_animations: elem
|
||||
.property_animations
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), duplicate_element_with_mapping(v, mapping, root_component)))
|
||||
.collect(),
|
||||
// We will do the fixup of the bindings later
|
||||
bindings: elem.bindings.clone(),
|
||||
children: elem
|
||||
.children
|
||||
|
@ -120,42 +139,54 @@ fn duplicate_element_with_mapping(
|
|||
repeated: elem.repeated.clone(),
|
||||
node: elem.node.clone(),
|
||||
enclosing_component: Rc::downgrade(root_component),
|
||||
states: elem.states.clone(),
|
||||
transitions: elem
|
||||
.transitions
|
||||
.iter()
|
||||
.map(|t| duplicate_transition(t, mapping, root_component))
|
||||
.collect(),
|
||||
}));
|
||||
mapping.insert(element_key(element), new.clone());
|
||||
new
|
||||
}
|
||||
|
||||
fn fixup_binding(
|
||||
val: &mut Expression,
|
||||
fn fixup_reference(
|
||||
NamedReference { element, .. }: &mut NamedReference,
|
||||
mapping: &HashMap<usize, ElementRc>,
|
||||
root_component: &Rc<Component>,
|
||||
) {
|
||||
val.visit_mut(|sub| fixup_binding(sub, mapping, root_component));
|
||||
*element =
|
||||
element.upgrade().and_then(|e| mapping.get(&element_key(&e))).map(Rc::downgrade).unwrap();
|
||||
}
|
||||
|
||||
fn fixup_binding(val: &mut Expression, mapping: &HashMap<usize, ElementRc>) {
|
||||
val.visit_mut(|sub| fixup_binding(sub, mapping));
|
||||
match val {
|
||||
Expression::PropertyReference(NamedReference { element, .. }) => {
|
||||
*element = element
|
||||
.upgrade()
|
||||
.and_then(|e| mapping.get(&element_key(&e)))
|
||||
.map(Rc::downgrade)
|
||||
.unwrap();
|
||||
}
|
||||
Expression::SignalReference(NamedReference { element, .. }) => {
|
||||
*element = element
|
||||
.upgrade()
|
||||
.and_then(|e| mapping.get(&element_key(&e)))
|
||||
.map(Rc::downgrade)
|
||||
.unwrap();
|
||||
}
|
||||
Expression::PropertyReference(r) => fixup_reference(r, mapping),
|
||||
Expression::SignalReference(r) => fixup_reference(r, mapping),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_binding(
|
||||
val: &Expression,
|
||||
mapping: &HashMap<usize, ElementRc>,
|
||||
root_component: &Rc<Component>,
|
||||
) -> Expression {
|
||||
fn fold_binding(val: &Expression, mapping: &HashMap<usize, ElementRc>) -> Expression {
|
||||
let mut new_val = val.clone();
|
||||
fixup_binding(&mut new_val, mapping, root_component);
|
||||
fixup_binding(&mut new_val, mapping);
|
||||
new_val
|
||||
}
|
||||
|
||||
fn duplicate_transition(
|
||||
t: &Transition,
|
||||
mapping: &mut HashMap<usize, Rc<RefCell<Element>>>,
|
||||
root_component: &Rc<Component>,
|
||||
) -> Transition {
|
||||
Transition {
|
||||
is_out: t.is_out,
|
||||
state_id: t.state_id.clone(),
|
||||
property_animations: t
|
||||
.property_animations
|
||||
.iter()
|
||||
.map(|(r, anim)| {
|
||||
(r.clone(), duplicate_element_with_mapping(anim, mapping, root_component))
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ pub fn create_repeater_components(component: &Rc<Component>) {
|
|||
repeated: None,
|
||||
node: elem.node.clone(),
|
||||
enclosing_component: Default::default(),
|
||||
states: Default::default(),
|
||||
transitions: Default::default(),
|
||||
})),
|
||||
parent_element,
|
||||
..Component::default()
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
|
||||
|
||||
SuperSimple := Rectangle {
|
||||
|
||||
animate x {
|
||||
|
|
40
sixtyfps_compiler/tests/basic/states_transitions.60
Normal file
40
sixtyfps_compiler/tests/basic/states_transitions.60
Normal file
|
@ -0,0 +1,40 @@
|
|||
TestCase := Rectangle {
|
||||
property<bool> checked;
|
||||
property <int32> border;
|
||||
states [
|
||||
checked when checked: {
|
||||
color: blue; // same as root.color
|
||||
text.color: red;
|
||||
border: 42;
|
||||
}
|
||||
pressed when touch.pressed: {
|
||||
color: green;
|
||||
border: 88;
|
||||
text.foo.bar: 0;
|
||||
/// ^error{'text.foo.bar' is not a valid property}
|
||||
colour: yellow;
|
||||
/// ^error{'colour' is not a valid property}
|
||||
fox.color: yellow;
|
||||
/// ^error{'fox' is not a valid element id}
|
||||
text.fox: yellow;
|
||||
/// ^error{'fox' not found in 'text'}
|
||||
}
|
||||
]
|
||||
|
||||
transitions [
|
||||
to pressed: {
|
||||
//animate * { duration: 88ms }
|
||||
animate color { duration: 88ms; }
|
||||
}
|
||||
out pressed: {
|
||||
//animate color, foo.x { duration: 300ms; }
|
||||
//pause: 20ms;
|
||||
animate border { duration: 120ms; }
|
||||
}
|
||||
]
|
||||
|
||||
text := Text {}
|
||||
touch := TouchArea {}
|
||||
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue