mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-04 18:58:36 +00:00
Transition: Introduce in-out to allow writing symmetry animation (#8509)
This commit is contained in:
parent
c18b3cf02f
commit
83db461f63
11 changed files with 168 additions and 37 deletions
|
@ -543,12 +543,19 @@ impl From<Type> for PropertyDeclaration {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum TransitionDirection {
|
||||
In,
|
||||
Out,
|
||||
InOut,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TransitionPropertyAnimation {
|
||||
/// The state id as computed in lower_state
|
||||
pub state_id: i32,
|
||||
/// false for 'to', true for 'out'
|
||||
pub is_out: bool,
|
||||
/// The direction of the transition
|
||||
pub direction: TransitionDirection,
|
||||
/// The content of the `animation` object
|
||||
pub animation: ElementRc,
|
||||
}
|
||||
|
@ -557,13 +564,42 @@ impl TransitionPropertyAnimation {
|
|||
/// Return an expression which returns a boolean which is true if the transition is active.
|
||||
/// The state argument is an expression referencing the state property of type StateInfo
|
||||
pub fn condition(&self, state: Expression) -> Expression {
|
||||
Expression::BinaryExpression {
|
||||
lhs: Box::new(Expression::StructFieldAccess {
|
||||
base: Box::new(state),
|
||||
name: (if self.is_out { "previous-state" } else { "current-state" }).into(),
|
||||
}),
|
||||
rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
|
||||
op: '=',
|
||||
match self.direction {
|
||||
TransitionDirection::In => Expression::BinaryExpression {
|
||||
lhs: Box::new(Expression::StructFieldAccess {
|
||||
base: Box::new(state),
|
||||
name: "current-state".into(),
|
||||
}),
|
||||
rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
|
||||
op: '=',
|
||||
},
|
||||
TransitionDirection::Out => Expression::BinaryExpression {
|
||||
lhs: Box::new(Expression::StructFieldAccess {
|
||||
base: Box::new(state),
|
||||
name: "previous-state".into(),
|
||||
}),
|
||||
rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
|
||||
op: '=',
|
||||
},
|
||||
TransitionDirection::InOut => Expression::BinaryExpression {
|
||||
lhs: Box::new(Expression::BinaryExpression {
|
||||
lhs: Box::new(Expression::StructFieldAccess {
|
||||
base: Box::new(state.clone()),
|
||||
name: "current-state".into(),
|
||||
}),
|
||||
rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
|
||||
op: '=',
|
||||
}),
|
||||
rhs: Box::new(Expression::BinaryExpression {
|
||||
lhs: Box::new(Expression::StructFieldAccess {
|
||||
base: Box::new(state),
|
||||
name: "previous-state".into(),
|
||||
}),
|
||||
rhs: Box::new(Expression::NumberLiteral(self.state_id as _, Unit::None)),
|
||||
op: '=',
|
||||
}),
|
||||
op: '|',
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -601,7 +637,7 @@ impl Clone for PropertyAnimation {
|
|||
.iter()
|
||||
.map(|t| TransitionPropertyAnimation {
|
||||
state_id: t.state_id,
|
||||
is_out: t.is_out,
|
||||
direction: t.direction,
|
||||
animation: deep_clone(&t.animation),
|
||||
})
|
||||
.collect(),
|
||||
|
@ -2447,8 +2483,7 @@ pub struct State {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Transition {
|
||||
/// false for 'to', true for 'out'
|
||||
pub is_out: bool,
|
||||
pub direction: TransitionDirection,
|
||||
pub state_id: SmolStr,
|
||||
pub property_animations: Vec<(NamedReference, SourceLocation, ElementRc)>,
|
||||
pub node: syntax_nodes::Transition,
|
||||
|
@ -2464,8 +2499,21 @@ impl Transition {
|
|||
if let Some(star) = trs.child_token(SyntaxKind::Star) {
|
||||
diag.push_error("catch-all not yet implemented".into(), &star);
|
||||
};
|
||||
let direction_text = trs
|
||||
.first_child_or_token()
|
||||
.and_then(|t| t.as_token().map(|tok| tok.text().to_string()))
|
||||
.unwrap_or_default();
|
||||
|
||||
Transition {
|
||||
is_out: parser::identifier_text(&trs).unwrap_or_default() == "out",
|
||||
direction: match direction_text.as_str() {
|
||||
"in" => TransitionDirection::In,
|
||||
"out" => TransitionDirection::Out,
|
||||
"in-out" => TransitionDirection::InOut,
|
||||
"in_out" => TransitionDirection::InOut,
|
||||
_ => {
|
||||
unreachable!("Unknown transition direction: '{}'", direction_text);
|
||||
}
|
||||
},
|
||||
state_id: trs
|
||||
.DeclaredIdentifier()
|
||||
.and_then(|x| parser::identifier_text(&x))
|
||||
|
|
|
@ -414,7 +414,7 @@ declare_syntax! {
|
|||
StatePropertyChange -> [ QualifiedName, BindingExpression ],
|
||||
/// `transitions: [...]`
|
||||
Transitions -> [*Transition],
|
||||
/// There is an identifier "in" or "out", the DeclaredIdentifier is the state name
|
||||
/// There is an identifier "in", "out", "in-out", the DeclaredIdentifier is the state name
|
||||
Transition -> [?DeclaredIdentifier, *PropertyAnimation],
|
||||
/// Export a set of declared components by name
|
||||
ExportsList -> [ *ExportSpecifier, ?Component, *StructDeclaration, ?ExportModule, *EnumDeclaration ],
|
||||
|
|
|
@ -535,10 +535,10 @@ fn parse_state(p: &mut impl Parser) -> bool {
|
|||
SyntaxKind::Eof => return false,
|
||||
_ => {
|
||||
if p.nth(1).kind() == SyntaxKind::LBrace
|
||||
&& matches!(p.peek().as_str(), "in" | "out")
|
||||
&& matches!(p.peek().as_str(), "in" | "out" | "in-out" | "in_out")
|
||||
{
|
||||
let mut p = p.start_node(SyntaxKind::Transition);
|
||||
p.consume(); // "in" or "out"
|
||||
p.consume(); // "in", "out" or "in-out"
|
||||
p.expect(SyntaxKind::LBrace);
|
||||
if !parse_transition_inner(&mut *p) {
|
||||
return false;
|
||||
|
@ -562,7 +562,7 @@ fn parse_state(p: &mut impl Parser) -> bool {
|
|||
#[cfg_attr(test, parser_test)]
|
||||
/// ```test,Transitions
|
||||
/// transitions []
|
||||
/// transitions [in checked: {animate x { duration: 88ms; }} out checked: {animate x { duration: 88ms; }}]
|
||||
/// transitions [in checked: {animate x { duration: 88ms; }} out checked: {animate x { duration: 88ms; }} in-out checked: {animate x { duration: 88ms; }}]
|
||||
/// ```
|
||||
fn parse_transitions(p: &mut impl Parser) {
|
||||
debug_assert_eq!(p.peek().as_str(), "transitions");
|
||||
|
@ -578,14 +578,15 @@ fn parse_transitions(p: &mut impl Parser) {
|
|||
/// in pressed : {}
|
||||
/// in pressed: { animate x { duration: 88ms; } }
|
||||
/// out pressed: { animate x { duration: 88ms; } }
|
||||
/// in-out pressed: { animate x { duration: 88ms; } }
|
||||
/// ```
|
||||
fn parse_transition(p: &mut impl Parser) -> bool {
|
||||
if !matches!(p.peek().as_str(), "in" | "out") {
|
||||
p.error("Expected 'in' or 'out' to declare a transition");
|
||||
if !matches!(p.peek().as_str(), "in" | "out" | "in-out" | "in_out") {
|
||||
p.error("Expected 'in', 'out', or 'in-out' to declare a transition");
|
||||
return false;
|
||||
}
|
||||
let mut p = p.start_node(SyntaxKind::Transition);
|
||||
p.consume(); // "in" or "out"
|
||||
p.consume(); // "in", "out" or "in-out"
|
||||
{
|
||||
let mut p = p.start_node(SyntaxKind::DeclaredIdentifier);
|
||||
p.expect(SyntaxKind::Identifier);
|
||||
|
|
|
@ -511,7 +511,7 @@ fn duplicate_property_animation(
|
|||
.iter()
|
||||
.map(|a| TransitionPropertyAnimation {
|
||||
state_id: a.state_id,
|
||||
is_out: a.is_out,
|
||||
direction: a.direction,
|
||||
animation: duplicate_element_with_mapping(
|
||||
&a.animation,
|
||||
mapping,
|
||||
|
@ -568,7 +568,7 @@ fn duplicate_transition(
|
|||
priority_delta: i32,
|
||||
) -> Transition {
|
||||
Transition {
|
||||
is_out: t.is_out,
|
||||
direction: t.direction,
|
||||
state_id: t.state_id.clone(),
|
||||
property_animations: t
|
||||
.property_animations
|
||||
|
|
|
@ -161,7 +161,7 @@ fn lower_transitions_in_element(
|
|||
|
||||
let t = TransitionPropertyAnimation {
|
||||
state_id: *state,
|
||||
is_out: transition.is_out,
|
||||
direction: transition.direction,
|
||||
animation,
|
||||
};
|
||||
props.entry(p).or_insert_with(|| (span.clone(), vec![])).1.push(t);
|
||||
|
|
|
@ -38,7 +38,10 @@ export TestCase := Rectangle {
|
|||
animate border { duration: 120ms; }
|
||||
animate color, text.text { duration: 300ms; }
|
||||
/// ^error{'text.text' is not a property that can be animated}
|
||||
}
|
||||
|
||||
in-out checked: {
|
||||
animate color { duration: 100ms; }
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -57,6 +60,10 @@ export component NewSyntax {
|
|||
color: blue; // same as root.color
|
||||
text.color: red;
|
||||
border: 42;
|
||||
|
||||
in-out {
|
||||
animate color { duration: 100ms; }
|
||||
}
|
||||
}
|
||||
pressed when touch.pressed: {
|
||||
color: green;
|
||||
|
|
|
@ -453,7 +453,7 @@ impl Snapshotter {
|
|||
.transitions
|
||||
.iter()
|
||||
.map(|t| object_tree::Transition {
|
||||
is_out: t.is_out,
|
||||
direction: t.direction,
|
||||
state_id: t.state_id.clone(),
|
||||
property_animations: t
|
||||
.property_animations
|
||||
|
@ -570,7 +570,7 @@ impl Snapshotter {
|
|||
.iter()
|
||||
.map(|tpa| object_tree::TransitionPropertyAnimation {
|
||||
state_id: tpa.state_id,
|
||||
is_out: tpa.is_out,
|
||||
direction: tpa.direction,
|
||||
animation: self.create_and_snapshot_element(&tpa.animation),
|
||||
})
|
||||
.collect(),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue