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 fyrox_graph::BaseSceneGraph;
42use std::ops::{Deref, DerefMut};
43
44#[derive(Debug, Clone, PartialEq)]
45pub enum AnimationPlayerMessage {
46 EnableAnimation { animation: String, enabled: bool },
47 RewindAnimation { animation: String },
48 TimePosition { animation: String, time: f32 },
49}
50
51impl AnimationPlayerMessage {
52 define_constructor!(
53 AnimationPlayerMessage:EnableAnimation => fn enable_animation(animation: String, enabled: bool), layout: false
55 );
56 define_constructor!(
57 AnimationPlayerMessage:RewindAnimation => fn rewind_animation(animation: String), layout: false
59 );
60 define_constructor!(
61 AnimationPlayerMessage:TimePosition => fn time_position(animation: String, time: f32), layout: false
63 );
64}
65
66pub type Animation = crate::generic_animation::Animation<Handle<UiNode>>;
68pub type Track = crate::generic_animation::track::Track;
70pub type AnimationContainer = crate::generic_animation::AnimationContainer<Handle<UiNode>>;
72pub type AnimationPose = crate::generic_animation::AnimationPose<Handle<UiNode>>;
74pub type NodePose = crate::generic_animation::NodePose<Handle<UiNode>>;
76
77pub mod prelude {
79 pub use super::{
80 Animation, AnimationContainer, AnimationContainerExt, AnimationPlayer,
81 AnimationPlayerBuilder, AnimationPose, AnimationPoseExt, BoundValueCollectionExt, NodePose,
82 Track,
83 };
84 pub use crate::generic_animation::{
85 container::{TrackDataContainer, TrackValueKind},
86 signal::AnimationSignal,
87 value::{BoundValueCollection, TrackValue, ValueBinding, ValueType},
88 AnimationEvent,
89 };
90}
91
92pub trait AnimationContainerExt {
94 fn update_animations(&mut self, nodes: &mut UserInterface, dt: f32);
97}
98
99impl AnimationContainerExt for AnimationContainer {
100 fn update_animations(&mut self, ui: &mut UserInterface, dt: f32) {
101 for animation in self.iter_mut().filter(|anim| anim.is_enabled()) {
102 animation.tick(dt);
103 animation.pose().apply(ui);
104 }
105 }
106}
107
108pub trait AnimationPoseExt {
110 fn apply(&self, ui: &mut UserInterface);
112}
113
114impl AnimationPoseExt for AnimationPose {
115 fn apply(&self, ui: &mut UserInterface) {
116 for (node, local_pose) in self.poses() {
117 if node.is_none() {
118 Log::writeln(MessageKind::Error, "Invalid node handle found for animation pose, most likely it means that animation retargeting failed!");
119 } else if let Some(node) = ui.try_get_mut(*node) {
120 node.invalidate_layout();
121
122 local_pose.values.apply(node);
123 }
124 }
125 }
126}
127
128pub trait BoundValueCollectionExt {
130 fn apply(&self, node_ref: &mut UiNode);
133}
134
135impl BoundValueCollectionExt for BoundValueCollection {
136 fn apply(&self, node_ref: &mut UiNode) {
137 for bound_value in self.values.iter() {
138 match bound_value.binding {
139 ValueBinding::Position => {
140 if let TrackValue::Vector2(v) = bound_value.value {
141 node_ref.set_desired_local_position(v);
142 } else {
143 Log::err(
144 "Unable to apply position, because underlying type is not Vector2!",
145 )
146 }
147 }
148 ValueBinding::Scale => Log::warn("Implement me!"),
149 ValueBinding::Rotation => Log::warn("Implement me!"),
150 ValueBinding::Property {
151 name: ref property_name,
152 value_type,
153 } => bound_value.apply_to_object(node_ref, property_name, value_type),
154 }
155 }
156 }
157}
158
159#[derive(Visit, Reflect, Clone, Debug, ComponentProvider)]
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}