1use crate::message::MessageData;
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_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};
41
42#[derive(Debug, Clone, PartialEq)]
43pub enum AnimationPlayerMessage {
44 EnableAnimation { animation: String, enabled: bool },
45 RewindAnimation { animation: String },
46 TimePosition { animation: String, time: f32 },
47}
48impl MessageData for AnimationPlayerMessage {}
49
50pub type Animation = crate::generic_animation::Animation<Handle<UiNode>>;
52pub type Track = crate::generic_animation::track::Track;
54pub type AnimationContainer = crate::generic_animation::AnimationContainer<Handle<UiNode>>;
56pub type AnimationPose = crate::generic_animation::AnimationPose<Handle<UiNode>>;
58pub type NodePose = crate::generic_animation::NodePose<Handle<UiNode>>;
60
61pub mod prelude {
63 pub use super::{
64 Animation, AnimationContainer, AnimationContainerExt, AnimationPlayer,
65 AnimationPlayerBuilder, AnimationPose, AnimationPoseExt, BoundValueCollectionExt, NodePose,
66 Track,
67 };
68 pub use crate::generic_animation::{
69 container::{TrackDataContainer, TrackValueKind},
70 signal::AnimationSignal,
71 value::{BoundValueCollection, TrackValue, ValueBinding, ValueType},
72 AnimationEvent,
73 };
74}
75
76pub trait AnimationContainerExt {
78 fn update_animations(&mut self, nodes: &mut UserInterface, dt: f32);
81}
82
83impl AnimationContainerExt for AnimationContainer {
84 fn update_animations(&mut self, ui: &mut UserInterface, dt: f32) {
85 for animation in self.iter_mut().filter(|anim| anim.is_enabled()) {
86 animation.tick(dt);
87 animation.pose().apply(ui);
88 }
89 }
90}
91
92pub trait AnimationPoseExt {
94 fn apply(&self, ui: &mut UserInterface);
96}
97
98impl AnimationPoseExt for AnimationPose {
99 fn apply(&self, ui: &mut UserInterface) {
100 for (node, local_pose) in self.poses() {
101 if node.is_none() {
102 Log::writeln(MessageKind::Error, "Invalid node handle found for animation pose, most likely it means that animation retargeting failed!");
103 } else if let Ok(node) = ui.try_get_node_mut(*node) {
104 node.invalidate_layout();
105
106 local_pose.values.apply(node);
107 }
108 }
109 }
110}
111
112pub trait BoundValueCollectionExt {
114 fn apply(&self, node_ref: &mut UiNode);
117}
118
119impl BoundValueCollectionExt for BoundValueCollection {
120 fn apply(&self, node_ref: &mut UiNode) {
121 for bound_value in self.values.iter() {
122 match bound_value.binding {
123 ValueBinding::Position => {
124 if let TrackValue::Vector2(v) = bound_value.value {
125 node_ref.set_desired_local_position(v);
126 } else {
127 Log::err(
128 "Unable to apply position, because underlying type is not Vector2!",
129 )
130 }
131 }
132 ValueBinding::Scale => Log::warn("Implement me!"),
133 ValueBinding::Rotation => Log::warn("Implement me!"),
134 ValueBinding::Property {
135 name: ref property_name,
136 value_type,
137 } => bound_value.apply_to_object(node_ref, property_name, value_type),
138 }
139 }
140 }
141}
142
143#[derive(Visit, Reflect, Clone, Debug, ComponentProvider)]
147#[reflect(derived_type = "UiNode")]
148pub struct AnimationPlayer {
149 widget: Widget,
150 #[component(include)]
151 pub(crate) animations: InheritableVariable<AnimationContainer>,
152 #[component(include)]
153 auto_apply: bool,
154}
155
156impl ConstructorProvider<UiNode, UserInterface> for AnimationPlayer {
157 fn constructor() -> GraphNodeConstructor<UiNode, UserInterface> {
158 GraphNodeConstructor::new::<Self>()
159 .with_variant("Animation Player", |ui| {
160 AnimationPlayerBuilder::new(WidgetBuilder::new().with_name("Animation Player"))
161 .build(&mut ui.build_ctx())
162 .to_base()
163 .into()
164 })
165 .with_group("Animation")
166 }
167}
168
169impl Default for AnimationPlayer {
170 fn default() -> Self {
171 Self {
172 widget: Default::default(),
173 animations: Default::default(),
174 auto_apply: true,
175 }
176 }
177}
178
179impl AnimationPlayer {
180 pub fn set_auto_apply(&mut self, auto_apply: bool) {
190 self.auto_apply = auto_apply;
191 }
192
193 pub fn is_auto_apply(&self) -> bool {
196 self.auto_apply
197 }
198
199 pub fn animations(&self) -> &InheritableVariable<AnimationContainer> {
201 &self.animations
202 }
203
204 pub fn animations_mut(&mut self) -> &mut InheritableVariable<AnimationContainer> {
207 &mut self.animations
208 }
209
210 pub fn set_animations(&mut self, animations: AnimationContainer) {
212 self.animations.set_value_and_mark_modified(animations);
213 }
214
215 fn find_animation(&mut self, name: &str) -> Option<&mut Animation> {
216 self.animations.find_by_name_mut(name).map(|(_, a)| a)
217 }
218}
219
220impl TypeUuidProvider for AnimationPlayer {
221 fn type_uuid() -> Uuid {
222 uuid!("44d1c94e-354f-4f9a-b918-9d31c28aa16a")
223 }
224}
225
226define_widget_deref!(AnimationPlayer);
227
228impl Control for AnimationPlayer {
229 fn update(&mut self, dt: f32, ui: &mut UserInterface) {
230 if self.auto_apply {
231 self.animations
232 .get_value_mut_silent()
233 .update_animations(ui, dt);
234 }
235 }
236
237 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
238 self.widget.handle_routed_message(ui, message);
239
240 if let Some(msg) = message.data::<AnimationPlayerMessage>() {
241 match msg {
242 AnimationPlayerMessage::EnableAnimation { animation, enabled } => {
243 if let Some(animation) = self.find_animation(animation) {
244 animation.set_enabled(*enabled);
245 }
246 }
247 AnimationPlayerMessage::RewindAnimation { animation } => {
248 if let Some(animation) = self.find_animation(animation) {
249 animation.rewind();
250 }
251 }
252 AnimationPlayerMessage::TimePosition { animation, time } => {
253 if let Some(animation) = self.find_animation(animation) {
254 animation.set_time_position(*time);
255 }
256 }
257 }
258 }
259 }
260}
261
262pub struct AnimationPlayerBuilder {
264 widget_builder: WidgetBuilder,
265 animations: AnimationContainer,
266 auto_apply: bool,
267}
268
269impl AnimationPlayerBuilder {
270 pub fn new(widget_builder: WidgetBuilder) -> Self {
272 Self {
273 widget_builder,
274 animations: AnimationContainer::new(),
275 auto_apply: true,
276 }
277 }
278
279 pub fn with_animations(mut self, animations: AnimationContainer) -> Self {
281 self.animations = animations;
282 self
283 }
284
285 pub fn with_auto_apply(mut self, auto_apply: bool) -> Self {
287 self.auto_apply = auto_apply;
288 self
289 }
290
291 pub fn build_animation_player(self, ctx: &BuildContext) -> AnimationPlayer {
293 AnimationPlayer {
294 widget: self.widget_builder.with_need_update(true).build(ctx),
295 animations: self.animations.into(),
296 auto_apply: self.auto_apply,
297 }
298 }
299
300 pub fn build_node(self, ctx: &BuildContext) -> UiNode {
302 UiNode::new(self.build_animation_player(ctx))
303 }
304
305 pub fn build(self, ctx: &mut BuildContext) -> Handle<AnimationPlayer> {
307 ctx.add(self.build_animation_player(ctx))
308 }
309}
310
311#[cfg(test)]
312mod test {
313 use crate::animation::AnimationPlayerBuilder;
314 use crate::{test::test_widget_deletion, widget::WidgetBuilder};
315
316 #[test]
317 fn test_deletion() {
318 test_widget_deletion(|ctx| AnimationPlayerBuilder::new(WidgetBuilder::new()).build(ctx));
319 }
320}