1use crate::MessageDirection;
25use crate::{
26 core::{
27 log::{Log, MessageKind},
28 pool::Handle,
29 reflect::prelude::*,
30 type_traits::prelude::*,
31 variable::InheritableVariable,
32 visitor::prelude::*,
33 },
34 define_constructor, define_widget_deref,
35 generic_animation::value::{BoundValueCollection, TrackValue, ValueBinding},
36 message::UiMessage,
37 widget::{Widget, WidgetBuilder},
38 BuildContext, Control, UiNode, UserInterface,
39};
40use fyrox_graph::constructor::{ConstructorProvider, GraphNodeConstructor};
41use std::ops::{Deref, DerefMut};
42
43#[derive(Debug, Clone, PartialEq)]
44pub enum AnimationPlayerMessage {
45 EnableAnimation { animation: String, enabled: bool },
46 RewindAnimation { animation: String },
47 TimePosition { animation: String, time: f32 },
48}
49
50impl AnimationPlayerMessage {
51 define_constructor!(
52 AnimationPlayerMessage:EnableAnimation => fn enable_animation(animation: String, enabled: bool), layout: false
54 );
55 define_constructor!(
56 AnimationPlayerMessage:RewindAnimation => fn rewind_animation(animation: String), layout: false
58 );
59 define_constructor!(
60 AnimationPlayerMessage:TimePosition => fn time_position(animation: String, time: f32), layout: false
62 );
63}
64
65pub type Animation = crate::generic_animation::Animation<Handle<UiNode>>;
67pub type Track = crate::generic_animation::track::Track;
69pub type AnimationContainer = crate::generic_animation::AnimationContainer<Handle<UiNode>>;
71pub type AnimationPose = crate::generic_animation::AnimationPose<Handle<UiNode>>;
73pub type NodePose = crate::generic_animation::NodePose<Handle<UiNode>>;
75
76pub mod prelude {
78 pub use super::{
79 Animation, AnimationContainer, AnimationContainerExt, AnimationPlayer,
80 AnimationPlayerBuilder, AnimationPose, AnimationPoseExt, BoundValueCollectionExt, NodePose,
81 Track,
82 };
83 pub use crate::generic_animation::{
84 container::{TrackDataContainer, TrackValueKind},
85 signal::AnimationSignal,
86 value::{BoundValueCollection, TrackValue, ValueBinding, ValueType},
87 AnimationEvent,
88 };
89}
90
91pub trait AnimationContainerExt {
93 fn update_animations(&mut self, nodes: &mut UserInterface, dt: f32);
96}
97
98impl AnimationContainerExt for AnimationContainer {
99 fn update_animations(&mut self, ui: &mut UserInterface, dt: f32) {
100 for animation in self.iter_mut().filter(|anim| anim.is_enabled()) {
101 animation.tick(dt);
102 animation.pose().apply(ui);
103 }
104 }
105}
106
107pub trait AnimationPoseExt {
109 fn apply(&self, ui: &mut UserInterface);
111}
112
113impl AnimationPoseExt for AnimationPose {
114 fn apply(&self, ui: &mut UserInterface) {
115 for (node, local_pose) in self.poses() {
116 if node.is_none() {
117 Log::writeln(MessageKind::Error, "Invalid node handle found for animation pose, most likely it means that animation retargeting failed!");
118 } else if let Some(node) = ui.try_get_node_mut(*node) {
119 node.invalidate_layout();
120
121 local_pose.values.apply(node);
122 }
123 }
124 }
125}
126
127pub trait BoundValueCollectionExt {
129 fn apply(&self, node_ref: &mut UiNode);
132}
133
134impl BoundValueCollectionExt for BoundValueCollection {
135 fn apply(&self, node_ref: &mut UiNode) {
136 for bound_value in self.values.iter() {
137 match bound_value.binding {
138 ValueBinding::Position => {
139 if let TrackValue::Vector2(v) = bound_value.value {
140 node_ref.set_desired_local_position(v);
141 } else {
142 Log::err(
143 "Unable to apply position, because underlying type is not Vector2!",
144 )
145 }
146 }
147 ValueBinding::Scale => Log::warn("Implement me!"),
148 ValueBinding::Rotation => Log::warn("Implement me!"),
149 ValueBinding::Property {
150 name: ref property_name,
151 value_type,
152 } => bound_value.apply_to_object(node_ref, property_name, value_type),
153 }
154 }
155 }
156}
157
158#[derive(Visit, Reflect, Clone, Debug, ComponentProvider)]
162#[reflect(derived_type = "UiNode")]
163pub struct AnimationPlayer {
164 widget: Widget,
165 #[component(include)]
166 pub(crate) animations: InheritableVariable<AnimationContainer>,
167 #[component(include)]
168 auto_apply: bool,
169}
170
171impl ConstructorProvider<UiNode, UserInterface> for AnimationPlayer {
172 fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
173 GraphNodeConstructor::new::<Self>()
174 .with_variant("Animation Player", |ui| {
175 AnimationPlayerBuilder::new(WidgetBuilder::new().with_name("Animation Player"))
176 .build(&mut ui.build_ctx())
177 .into()
178 })
179 .with_group("Animation")
180 }
181}
182
183impl Default for AnimationPlayer {
184 fn default() -> Self {
185 Self {
186 widget: Default::default(),
187 animations: Default::default(),
188 auto_apply: true,
189 }
190 }
191}
192
193impl AnimationPlayer {
194 pub fn set_auto_apply(&mut self, auto_apply: bool) {
204 self.auto_apply = auto_apply;
205 }
206
207 pub fn is_auto_apply(&self) -> bool {
210 self.auto_apply
211 }
212
213 pub fn animations(&self) -> &InheritableVariable<AnimationContainer> {
215 &self.animations
216 }
217
218 pub fn animations_mut(&mut self) -> &mut InheritableVariable<AnimationContainer> {
221 &mut self.animations
222 }
223
224 pub fn set_animations(&mut self, animations: AnimationContainer) {
226 self.animations.set_value_and_mark_modified(animations);
227 }
228
229 fn find_animation(&mut self, name: &str) -> Option<&mut Animation> {
230 self.animations.find_by_name_mut(name).map(|(_, a)| a)
231 }
232}
233
234impl TypeUuidProvider for AnimationPlayer {
235 fn type_uuid() -> Uuid {
236 uuid!("44d1c94e-354f-4f9a-b918-9d31c28aa16a")
237 }
238}
239
240define_widget_deref!(AnimationPlayer);
241
242impl Control for AnimationPlayer {
243 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
244 self.widget.handle_routed_message(ui, message);
245
246 if let Some(msg) = message.data::<AnimationPlayerMessage>() {
247 match msg {
248 AnimationPlayerMessage::EnableAnimation { animation, enabled } => {
249 if let Some(animation) = self.find_animation(animation) {
250 animation.set_enabled(*enabled);
251 }
252 }
253 AnimationPlayerMessage::RewindAnimation { animation } => {
254 if let Some(animation) = self.find_animation(animation) {
255 animation.rewind();
256 }
257 }
258 AnimationPlayerMessage::TimePosition { animation, time } => {
259 if let Some(animation) = self.find_animation(animation) {
260 animation.set_time_position(*time);
261 }
262 }
263 }
264 }
265 }
266
267 fn update(&mut self, dt: f32, ui: &mut UserInterface) {
268 if self.auto_apply {
269 self.animations
270 .get_value_mut_silent()
271 .update_animations(ui, dt);
272 }
273 }
274}
275
276pub struct AnimationPlayerBuilder {
278 widget_builder: WidgetBuilder,
279 animations: AnimationContainer,
280 auto_apply: bool,
281}
282
283impl AnimationPlayerBuilder {
284 pub fn new(widget_builder: WidgetBuilder) -> Self {
286 Self {
287 widget_builder,
288 animations: AnimationContainer::new(),
289 auto_apply: true,
290 }
291 }
292
293 pub fn with_animations(mut self, animations: AnimationContainer) -> Self {
295 self.animations = animations;
296 self
297 }
298
299 pub fn with_auto_apply(mut self, auto_apply: bool) -> Self {
301 self.auto_apply = auto_apply;
302 self
303 }
304
305 pub fn build_node(self, ctx: &BuildContext) -> UiNode {
307 UiNode::new(AnimationPlayer {
308 widget: self.widget_builder.with_need_update(true).build(ctx),
309 animations: self.animations.into(),
310 auto_apply: self.auto_apply,
311 })
312 }
313
314 pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
316 ctx.add_node(self.build_node(ctx))
317 }
318}
319
320#[cfg(test)]
321mod test {
322 use crate::animation::AnimationPlayerBuilder;
323 use crate::{test::test_widget_deletion, widget::WidgetBuilder};
324
325 #[test]
326 fn test_deletion() {
327 test_widget_deletion(|ctx| AnimationPlayerBuilder::new(WidgetBuilder::new()).build(ctx));
328 }
329}