1use super::interaction_tracker_inertia_modifier::InteractionTrackerInertiaModifier;
5use super::visual_interaction_source::VisualInteractionSource;
6use crate::animation::{PlaybackDirection, TimeSpan};
7use crate::media::MediaContext;
8use crate::numerics::Vector3;
9use crate::rendering::composition::animations::{
10 AnimationIterationBehavior, ExpressionAnimation, KeyFrame, KeyFrameAnimation, PropertySetSnapshot,
11 ScalarKeyFrameAnimation, Vector3KeyFrameAnimation,
12};
13use crate::rendering::composition::expressions::{
14 BuiltInExpressionFfi, Expression, ExpressionEvaluationContext, ExpressionVariant, ExpressionVariantValue,
15 IExpressionObject,
16};
17use crate::rendering::composition::{AsCompositionObject, CompositionObject, CompositionPropertySet, Compositor};
18use std::cell::{Cell, RefCell};
19use std::collections::VecDeque;
20use std::rc::{Rc, Weak};
21use std::time::Duration;
22
23pub const DEFAULT_INERTIA_DECAY_RATE: f32 = 0.95;
26
27const OVERPAN_LIMIT: f32 = 120.0;
30
31const REST_EPSILON: f32 = 0.01;
34
35#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
40pub enum InteractionTrackerState {
41 #[default]
43 Idle,
44 Interacting,
46 Inertia,
48 CustomAnimation,
51}
52
53#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
56pub enum InteractionTrackerClampingOption {
57 #[default]
59 Auto,
60 Disabled,
62}
63
64#[derive(Clone, Copy, Debug, PartialEq)]
66pub struct InteractionTrackerCustomAnimationStateEnteredArgs {
67 pub request_id: i32,
68 pub is_from_binding: bool,
70}
71
72#[derive(Clone, Copy, Debug, PartialEq)]
74pub struct InteractionTrackerIdleStateEnteredArgs {
75 pub request_id: i32,
76 pub is_from_binding: bool,
78}
79
80#[derive(Clone, Copy, Debug, PartialEq)]
82pub struct InteractionTrackerInertiaStateEnteredArgs {
83 pub modified_resting_position: Option<Vector3>,
85 pub modified_resting_scale: Option<f32>,
87 pub natural_resting_position: Vector3,
89 pub natural_resting_scale: f32,
91 pub position_velocity_in_pixels_per_second: Vector3,
92 pub scale_velocity_in_percent_per_second: f32,
93 pub request_id: i32,
94 pub is_inertia_from_impulse: bool,
97 pub is_from_binding: bool,
99}
100
101#[derive(Clone, Copy, Debug, PartialEq)]
103pub struct InteractionTrackerInteractingStateEnteredArgs {
104 pub request_id: i32,
105 pub is_from_binding: bool,
107}
108
109#[derive(Clone, Copy, Debug, PartialEq)]
111pub struct InteractionTrackerRequestIgnoredArgs {
112 pub request_id: i32,
113}
114
115#[derive(Clone, Copy, Debug, PartialEq)]
117pub struct InteractionTrackerValuesChangedArgs {
118 pub position: Vector3,
119 pub scale: f32,
120 pub request_id: i32,
121}
122
123pub trait IInteractionTrackerOwner {
134 fn custom_animation_state_entered(
135 &self,
136 _sender: &Rc<InteractionTracker>,
137 _args: &InteractionTrackerCustomAnimationStateEnteredArgs,
138 ) {
139 }
140
141 fn idle_state_entered(&self, _sender: &Rc<InteractionTracker>, _args: &InteractionTrackerIdleStateEnteredArgs) {}
142
143 fn inertia_state_entered(
144 &self,
145 _sender: &Rc<InteractionTracker>,
146 _args: &InteractionTrackerInertiaStateEnteredArgs,
147 ) {
148 }
149
150 fn interacting_state_entered(
151 &self,
152 _sender: &Rc<InteractionTracker>,
153 _args: &InteractionTrackerInteractingStateEnteredArgs,
154 ) {
155 }
156
157 fn request_ignored(&self, _sender: &Rc<InteractionTracker>, _args: &InteractionTrackerRequestIgnoredArgs) {}
158
159 fn values_changed(&self, _sender: &Rc<InteractionTracker>, _args: &InteractionTrackerValuesChangedArgs) {}
160}
161
162#[derive(Clone)]
173pub enum InteractionTrackerAnimation {
174 Vector3KeyFrame(Rc<Vector3KeyFrameAnimation>),
176 ScalarKeyFrame(Rc<ScalarKeyFrameAnimation>),
178 Expression(Rc<ExpressionAnimation>),
181}
182
183impl From<Rc<Vector3KeyFrameAnimation>> for InteractionTrackerAnimation {
184 fn from(value: Rc<Vector3KeyFrameAnimation>) -> Self {
185 Self::Vector3KeyFrame(value)
186 }
187}
188
189impl From<Rc<ScalarKeyFrameAnimation>> for InteractionTrackerAnimation {
190 fn from(value: Rc<ScalarKeyFrameAnimation>) -> Self {
191 Self::ScalarKeyFrame(value)
192 }
193}
194
195impl From<Rc<ExpressionAnimation>> for InteractionTrackerAnimation {
196 fn from(value: Rc<ExpressionAnimation>) -> Self {
197 Self::Expression(value)
198 }
199}
200
201#[derive(Clone, Copy, Debug, PartialEq)]
204pub(crate) struct TrackerValues {
205 pub position: Vector3,
206 pub position_velocity: Vector3,
207 pub scale: f32,
208 pub scale_velocity: f32,
209 pub min_position: Vector3,
210 pub max_position: Vector3,
211 pub min_scale: f32,
212 pub max_scale: f32,
213 pub natural_resting_position: Vector3,
214 pub natural_resting_scale: f32,
215 pub position_inertia_decay_rate: Option<Vector3>,
216 pub scale_inertia_decay_rate: Option<f32>,
217 pub is_position_rounding_suggested: bool,
218 pub is_inertia_from_impulse: bool,
219}
220
221impl Default for TrackerValues {
222 fn default() -> Self {
223 Self {
224 position: Vector3::ZERO,
225 position_velocity: Vector3::ZERO,
226 scale: 1.0,
227 scale_velocity: 0.0,
228 min_position: Vector3::ZERO,
229 max_position: Vector3::ZERO,
230 min_scale: 1.0,
231 max_scale: 1.0,
232 natural_resting_position: Vector3::ZERO,
233 natural_resting_scale: 1.0,
234 position_inertia_decay_rate: None,
235 scale_inertia_decay_rate: None,
236 is_position_rounding_suggested: true,
237 is_inertia_from_impulse: false,
238 }
239 }
240}
241
242impl TrackerValues {
243 fn effective_position_decay_rate(&self) -> Vector3 {
244 self.position_inertia_decay_rate.unwrap_or(Vector3::from_value(DEFAULT_INERTIA_DECAY_RATE))
245 }
246
247 fn effective_scale_decay_rate(&self) -> f32 {
248 self.scale_inertia_decay_rate.unwrap_or(DEFAULT_INERTIA_DECAY_RATE)
249 }
250
251 pub fn get_property(&self, name: &str) -> ExpressionVariant {
253 match name {
254 "Position" => self.position.into(),
255 "PositionVelocityInPixelsPerSecond" => self.position_velocity.into(),
256 "Scale" => self.scale.into(),
257 "ScaleVelocityInPercentPerSecond" => self.scale_velocity.into(),
258 "MinPosition" => self.min_position.into(),
259 "MaxPosition" => self.max_position.into(),
260 "MinScale" => self.min_scale.into(),
261 "MaxScale" => self.max_scale.into(),
262 "NaturalRestingPosition" => self.natural_resting_position.into(),
263 "NaturalRestingScale" => self.natural_resting_scale.into(),
264 "PositionInertiaDecayRate" => self.effective_position_decay_rate().into(),
265 "ScaleInertiaDecayRate" => self.effective_scale_decay_rate().into(),
266 "IsPositionRoundingSuggested" => self.is_position_rounding_suggested.into(),
267 "IsInertiaFromImpulse" => self.is_inertia_from_impulse.into(),
268 _ => ExpressionVariant::Invalid,
269 }
270 }
271}
272
273struct TrackerTarget(TrackerValues);
275
276impl IExpressionObject for TrackerTarget {
277 fn get_property(&self, name: &str) -> ExpressionVariant {
278 self.0.get_property(name)
279 }
280}
281
282fn evaluate_animation(animation: &ExpressionAnimation, values: TrackerValues, current: ExpressionVariant) -> ExpressionVariant {
287 let Some(source) = animation.expression() else { return ExpressionVariant::Invalid };
288 let Ok(expression) = Expression::parse(&source) else { return ExpressionVariant::Invalid };
289 evaluate_parsed(&expression, animation.create_snapshot(), values, current, current)
290}
291
292fn evaluate_parsed(
293 expression: &Expression,
294 parameters: Rc<PropertySetSnapshot>,
295 values: TrackerValues,
296 starting: ExpressionVariant,
297 current: ExpressionVariant,
298) -> ExpressionVariant {
299 let target = TrackerTarget(values);
300 let ctx = ExpressionEvaluationContext {
301 starting_value: starting,
302 current_value: current,
303 final_value: starting,
304 target: Some(&target),
305 parameters: Some(&*parameters),
306 foreign_function_interface: Some(BuiltInExpressionFfi::instance()),
307 };
308 expression.evaluate(&ctx)
309}
310
311fn axis(v: Vector3, index: usize) -> f32 {
312 match index {
313 0 => v.x,
314 1 => v.y,
315 _ => v.z,
316 }
317}
318
319fn set_axis(v: &mut Vector3, index: usize, value: f32) {
320 match index {
321 0 => v.x = value,
322 1 => v.y = value,
323 _ => v.z = value,
324 }
325}
326
327fn clamp_scalar(value: f32, min: f32, max: f32) -> f32 {
330 let max = max.max(min);
331 if value < min {
332 min
333 } else if value > max {
334 max
335 } else {
336 value
337 }
338}
339
340fn clamp_vector(value: Vector3, min: Vector3, max: Vector3) -> Vector3 {
341 Vector3::new(
342 clamp_scalar(value.x, min.x, max.x),
343 clamp_scalar(value.y, min.y, max.y),
344 clamp_scalar(value.z, min.z, max.z),
345 )
346}
347
348fn rubber_band(x: f32) -> f32 {
351 OVERPAN_LIMIT * (1.0 - 1.0 / (1.0 + 0.55 * x / OVERPAN_LIMIT))
352}
353
354fn rubber_band_scalar(raw: f32, min: f32, max: f32) -> f32 {
355 let max = max.max(min);
356 if raw < min {
357 min - rubber_band(min - raw)
358 } else if raw > max {
359 max + rubber_band(raw - max)
360 } else {
361 raw
362 }
363}
364
365fn retention(decay_rate: f32) -> f32 {
372 (1.0 - decay_rate.clamp(0.0, 1.0)).min(0.9999)
373}
374
375pub(crate) fn natural_resting_value(p: f32, v: f32, r: f32) -> f32 {
380 if r <= 0.0 || v == 0.0 {
381 p
382 } else {
383 p - v / r.ln()
384 }
385}
386
387fn exponential_motion(start: f32, target: f32, r: f32, t: f32) -> (f32, f32, bool) {
392 if r <= 0.0 {
393 return (target, 0.0, true);
394 }
395 let k = r.powf(t);
396 let remaining = (start - target) * k;
397 let velocity = (target - start) * (-r.ln()) * k;
398 (target + remaining, velocity, remaining.abs() < REST_EPSILON)
399}
400
401trait TrackValue: ExpressionVariantValue {
403 fn interpolate(from: Self, to: Self, progress: f32) -> Self;
404}
405
406impl TrackValue for Vector3 {
407 fn interpolate(from: Self, to: Self, progress: f32) -> Self {
408 from + (to - from) * progress
409 }
410}
411
412impl TrackValue for f32 {
413 fn interpolate(from: Self, to: Self, progress: f32) -> Self {
414 from + (to - from) * progress
415 }
416}
417
418struct KeyFrameTrack<T> {
421 frames: Vec<KeyFrame<T>>,
422 duration: f32,
423 delay: f32,
424 iteration_count: i32,
425 forever: bool,
426 direction: PlaybackDirection,
427 parameters: Rc<PropertySetSnapshot>,
428}
429
430impl<T: TrackValue> KeyFrameTrack<T> {
431 fn new(animation: &KeyFrameAnimation) -> Self {
432 let frames = animation.with_key_frames::<T, _>(|key_frames| key_frames.items().to_vec());
433 KeyFrameTrack {
434 frames,
435 duration: animation.duration().as_secs_f32().max(0.001),
436 delay: animation.delay_time().as_secs_f32(),
437 iteration_count: animation.iteration_count().max(1),
438 forever: animation.iteration_behavior() == AnimationIterationBehavior::Forever,
439 direction: animation.direction(),
440 parameters: animation.create_snapshot(),
441 }
442 }
443
444 fn value_at(&self, elapsed: f32, starting: T, current: T, values: TrackerValues) -> (T, bool) {
446 let t = elapsed - self.delay;
447 if t < 0.0 {
448 return (starting, false);
449 }
450 let total = self.duration * self.iteration_count as f32;
451 let (iteration, local, finished) = if !self.forever && t >= total {
452 (self.iteration_count - 1, 1.0, true)
453 } else {
454 let iteration = (t / self.duration).floor();
455 (iteration as i32, (t - iteration * self.duration) / self.duration, false)
456 };
457 let even = iteration % 2 == 0;
458 let progress = match self.direction {
459 PlaybackDirection::Normal => local,
460 PlaybackDirection::Reverse => 1.0 - local,
461 PlaybackDirection::Alternate => {
462 if even {
463 local
464 } else {
465 1.0 - local
466 }
467 }
468 PlaybackDirection::AlternateReverse => {
469 if even {
470 1.0 - local
471 } else {
472 local
473 }
474 }
475 };
476 (self.interpolate(progress, starting, current, values), finished)
477 }
478
479 fn frame_value(&self, frame: &KeyFrame<T>, starting: T, current: T, values: TrackerValues) -> T {
480 match &frame.expression {
481 Some(expression) => evaluate_parsed(
482 expression,
483 self.parameters.clone(),
484 values,
485 ExpressionVariant::create(starting),
486 ExpressionVariant::create(current),
487 )
488 .try_cast::<T>()
489 .unwrap_or(frame.value),
490 None => frame.value,
491 }
492 }
493
494 fn interpolate(&self, progress: f32, starting: T, current: T, values: TrackerValues) -> T {
495 let mut previous_key = 0.0f32;
498 let mut previous_value = starting;
499 for frame in &self.frames {
500 let value = self.frame_value(frame, starting, current, values);
501 if progress <= frame.normalized_progress_key {
502 let span = frame.normalized_progress_key - previous_key;
503 let local = if span <= 0.0 { 1.0 } else { (progress - previous_key) / span };
504 let eased = frame.easing_function.ease(local as f64) as f32;
505 return T::interpolate(previous_value, value, eased);
506 }
507 previous_key = frame.normalized_progress_key;
508 previous_value = value;
509 }
510 previous_value
511 }
512}
513
514enum Track<T> {
516 KeyFrames(KeyFrameTrack<T>),
517 Expression { expression: Rc<Expression>, animation: Rc<ExpressionAnimation> },
518}
519
520impl<T: TrackValue> Track<T> {
521 fn value_at(&self, elapsed: f32, starting: T, current: T, values: TrackerValues) -> (T, bool) {
522 match self {
523 Track::KeyFrames(track) => track.value_at(elapsed, starting, current, values),
524 Track::Expression { expression, animation } => {
525 let value = evaluate_parsed(
526 expression,
527 animation.create_snapshot(),
528 values,
529 ExpressionVariant::create(starting),
530 ExpressionVariant::create(current),
531 )
532 .try_cast::<T>()
533 .unwrap_or(current);
534 (value, false)
535 }
536 }
537 }
538}
539
540enum CustomKind {
541 Position { track: Track<Vector3>, starting: Vector3 },
542 Scale { track: Track<f32>, starting: f32, center: Vector3 },
543}
544
545struct CustomRun {
546 request_id: i32,
547 elapsed: f32,
548 kind: CustomKind,
549}
550
551struct InertiaRun {
552 request_id: i32,
553 elapsed: f32,
554 start_position: Vector3,
555 target_position: Vector3,
556 position_retention: Vector3,
557 start_scale: f32,
558 target_scale: f32,
559 scale_retention: f32,
560 scale_center: Vector3,
561 motions: [Option<Rc<ExpressionAnimation>>; 3],
563}
564
565enum Notification {
566 CustomAnimation(InteractionTrackerCustomAnimationStateEnteredArgs),
567 Idle(InteractionTrackerIdleStateEnteredArgs),
568 Inertia(InteractionTrackerInertiaStateEnteredArgs),
569 Interacting(InteractionTrackerInteractingStateEnteredArgs),
570 RequestIgnored(InteractionTrackerRequestIgnoredArgs),
571 ValuesChanged(InteractionTrackerValuesChangedArgs),
572}
573
574pub struct CompositionInteractionSourceCollection {
576 tracker: Weak<InteractionTracker>,
577 items: RefCell<Vec<Rc<VisualInteractionSource>>>,
578}
579
580impl CompositionInteractionSourceCollection {
581 pub fn add(&self, source: Rc<VisualInteractionSource>) {
584 if self.items.borrow().iter().any(|s| Rc::ptr_eq(s, &source)) {
585 return;
586 }
587 self.items.borrow_mut().push(source.clone());
588 source.attach_tracker(&self.tracker);
589 }
590
591 pub fn remove(&self, source: &Rc<VisualInteractionSource>) {
593 let removed = {
594 let mut items = self.items.borrow_mut();
595 let index = items.iter().position(|s| Rc::ptr_eq(s, source));
596 index.map(|index| items.remove(index))
597 };
598 if let Some(removed) = removed {
599 removed.detach_tracker(&self.tracker);
600 }
601 }
602
603 pub fn remove_all(&self) {
605 let removed = std::mem::take(&mut *self.items.borrow_mut());
606 for source in removed {
607 source.detach_tracker(&self.tracker);
608 }
609 }
610
611 pub fn count(&self) -> usize {
613 self.items.borrow().len()
614 }
615
616 pub fn items(&self) -> Vec<Rc<VisualInteractionSource>> {
618 self.items.borrow().clone()
619 }
620}
621
622pub struct InteractionTracker {
637 this: Weak<InteractionTracker>,
638 properties: Rc<CompositionPropertySet>,
639 owner: Option<Rc<dyn IInteractionTrackerOwner>>,
640 state: Cell<InteractionTrackerState>,
641 values: Cell<TrackerValues>,
642 interaction_sources: CompositionInteractionSourceCollection,
643 position_x_modifiers: RefCell<Vec<InteractionTrackerInertiaModifier>>,
644 position_y_modifiers: RefCell<Vec<InteractionTrackerInertiaModifier>>,
645 scale_modifiers: RefCell<Vec<InteractionTrackerInertiaModifier>>,
646 next_request_id: Cell<i32>,
647 last_request_id: Cell<i32>,
648 active_interactions: Cell<u32>,
650 raw_position: Cell<Vector3>,
653 inertia: RefCell<Option<InertiaRun>>,
654 custom: RefCell<Option<CustomRun>>,
655 frame_requested: Cell<bool>,
656 frame_requests_enabled: Cell<bool>,
657 last_frame_time: Cell<Option<f64>>,
658 notifications: RefCell<VecDeque<Notification>>,
659 notifying: Cell<bool>,
660}
661
662impl InteractionTracker {
663 pub fn create(compositor: &Rc<Compositor>) -> Rc<InteractionTracker> {
665 Self::create_core(compositor, None)
666 }
667
668 pub fn create_with_owner(compositor: &Rc<Compositor>, owner: Rc<dyn IInteractionTrackerOwner>) -> Rc<InteractionTracker> {
670 Self::create_core(compositor, Some(owner))
671 }
672
673 fn create_core(compositor: &Rc<Compositor>, owner: Option<Rc<dyn IInteractionTrackerOwner>>) -> Rc<InteractionTracker> {
674 let tracker = Rc::new_cyclic(|this: &Weak<InteractionTracker>| InteractionTracker {
675 this: this.clone(),
676 properties: CompositionPropertySet::new(compositor),
677 owner,
678 state: Cell::new(InteractionTrackerState::Idle),
679 values: Cell::new(TrackerValues::default()),
680 interaction_sources: CompositionInteractionSourceCollection {
681 tracker: this.clone(),
682 items: RefCell::new(Vec::new()),
683 },
684 position_x_modifiers: RefCell::new(Vec::new()),
685 position_y_modifiers: RefCell::new(Vec::new()),
686 scale_modifiers: RefCell::new(Vec::new()),
687 next_request_id: Cell::new(0),
688 last_request_id: Cell::new(0),
689 active_interactions: Cell::new(0),
690 raw_position: Cell::new(Vector3::ZERO),
691 inertia: RefCell::new(None),
692 custom: RefCell::new(None),
693 frame_requested: Cell::new(false),
694 frame_requests_enabled: Cell::new(true),
695 last_frame_time: Cell::new(None),
696 notifications: RefCell::new(VecDeque::new()),
697 notifying: Cell::new(false),
698 });
699 tracker.publish();
700 tracker
701 }
702
703 pub fn compositor(&self) -> &Rc<Compositor> {
705 self.properties.compositor()
706 }
707
708 pub fn properties(&self) -> &Rc<CompositionPropertySet> {
721 &self.properties
722 }
723
724 pub fn owner(&self) -> Option<Rc<dyn IInteractionTrackerOwner>> {
726 self.owner.clone()
727 }
728
729 pub fn state(&self) -> InteractionTrackerState {
732 self.state.get()
733 }
734
735 pub fn interaction_sources(&self) -> &CompositionInteractionSourceCollection {
737 &self.interaction_sources
738 }
739
740 fn update_values(&self, f: impl FnOnce(&mut TrackerValues)) {
743 let mut values = self.values.get();
744 f(&mut values);
745 self.values.set(values);
746 self.publish();
747 }
748
749 fn publish(&self) {
751 let v = self.values.get();
752 let p = &self.properties;
753 p.insert_vector3("Position", v.position);
754 p.insert_vector3("PositionVelocityInPixelsPerSecond", v.position_velocity);
755 p.insert_scalar("Scale", v.scale);
756 p.insert_scalar("ScaleVelocityInPercentPerSecond", v.scale_velocity);
757 p.insert_vector3("MinPosition", v.min_position);
758 p.insert_vector3("MaxPosition", v.max_position);
759 p.insert_scalar("MinScale", v.min_scale);
760 p.insert_scalar("MaxScale", v.max_scale);
761 p.insert_vector3("NaturalRestingPosition", v.natural_resting_position);
762 p.insert_scalar("NaturalRestingScale", v.natural_resting_scale);
763 p.insert_vector3("PositionInertiaDecayRate", v.effective_position_decay_rate());
764 p.insert_scalar("ScaleInertiaDecayRate", v.effective_scale_decay_rate());
765 p.insert_boolean("IsPositionRoundingSuggested", v.is_position_rounding_suggested);
766 p.insert_boolean("IsInertiaFromImpulse", v.is_inertia_from_impulse);
767 }
768
769 pub fn position(&self) -> Vector3 {
771 self.values.get().position
772 }
773
774 pub fn position_velocity_in_pixels_per_second(&self) -> Vector3 {
776 self.values.get().position_velocity
777 }
778
779 pub fn scale(&self) -> f32 {
781 self.values.get().scale
782 }
783
784 pub fn scale_velocity_in_percent_per_second(&self) -> f32 {
787 self.values.get().scale_velocity
788 }
789
790 pub fn min_position(&self) -> Vector3 {
792 self.values.get().min_position
793 }
794
795 pub fn set_min_position(&self, value: Vector3) {
798 self.update_values(|v| v.min_position = value);
799 }
800
801 pub fn max_position(&self) -> Vector3 {
803 self.values.get().max_position
804 }
805
806 pub fn set_max_position(&self, value: Vector3) {
807 self.update_values(|v| v.max_position = value);
808 }
809
810 pub fn min_scale(&self) -> f32 {
812 self.values.get().min_scale
813 }
814
815 pub fn set_min_scale(&self, value: f32) {
816 self.update_values(|v| v.min_scale = value);
817 }
818
819 pub fn max_scale(&self) -> f32 {
821 self.values.get().max_scale
822 }
823
824 pub fn set_max_scale(&self, value: f32) {
825 self.update_values(|v| v.max_scale = value);
826 }
827
828 pub fn natural_resting_position(&self) -> Vector3 {
832 self.values.get().natural_resting_position
833 }
834
835 pub fn natural_resting_scale(&self) -> f32 {
837 self.values.get().natural_resting_scale
838 }
839
840 pub fn position_inertia_decay_rate(&self) -> Option<Vector3> {
844 self.values.get().position_inertia_decay_rate
845 }
846
847 pub fn set_position_inertia_decay_rate(&self, value: Option<Vector3>) {
848 self.update_values(|v| v.position_inertia_decay_rate = value);
849 }
850
851 pub fn scale_inertia_decay_rate(&self) -> Option<f32> {
853 self.values.get().scale_inertia_decay_rate
854 }
855
856 pub fn set_scale_inertia_decay_rate(&self, value: Option<f32>) {
857 self.update_values(|v| v.scale_inertia_decay_rate = value);
858 }
859
860 pub fn is_position_rounding_suggested(&self) -> bool {
863 self.values.get().is_position_rounding_suggested
864 }
865
866 pub fn is_inertia_from_impulse(&self) -> bool {
869 self.values.get().is_inertia_from_impulse
870 }
871
872 pub fn configure_position_x_inertia_modifiers(&self, modifiers: impl IntoIterator<Item = InteractionTrackerInertiaModifier>) {
877 *self.position_x_modifiers.borrow_mut() = modifiers.into_iter().collect();
878 }
879
880 pub fn configure_position_y_inertia_modifiers(&self, modifiers: impl IntoIterator<Item = InteractionTrackerInertiaModifier>) {
882 *self.position_y_modifiers.borrow_mut() = modifiers.into_iter().collect();
883 }
884
885 pub fn configure_scale_inertia_modifiers(&self, modifiers: impl IntoIterator<Item = InteractionTrackerInertiaModifier>) {
887 *self.scale_modifiers.borrow_mut() = modifiers.into_iter().collect();
888 }
889
890 fn new_request_id(&self) -> i32 {
893 let id = self.next_request_id.get() + 1;
894 self.next_request_id.set(id);
895 self.last_request_id.set(id);
896 id
897 }
898
899 fn ignore_while_interacting(&self, request_id: i32) -> bool {
902 if self.state.get() == InteractionTrackerState::Interacting {
903 self.notify(Notification::RequestIgnored(InteractionTrackerRequestIgnoredArgs { request_id }));
904 return true;
905 }
906 false
907 }
908
909 pub fn try_update_position(&self, value: Vector3) -> i32 {
913 self.try_update_position_with_option(value, InteractionTrackerClampingOption::Auto)
914 }
915
916 pub fn try_update_position_with_option(&self, value: Vector3, option: InteractionTrackerClampingOption) -> i32 {
918 let id = self.new_request_id();
919 if !self.ignore_while_interacting(id) {
920 self.jump_to(value, option, id);
921 }
922 self.flush();
923 id
924 }
925
926 pub fn try_update_position_by(&self, amount: Vector3) -> i32 {
928 let id = self.new_request_id();
929 if !self.ignore_while_interacting(id) {
930 self.jump_to(self.position() + amount, InteractionTrackerClampingOption::Auto, id);
931 }
932 self.flush();
933 id
934 }
935
936 fn jump_to(&self, value: Vector3, option: InteractionTrackerClampingOption, request_id: i32) {
937 self.stop_runs();
938 let values = self.values.get();
939 let position = match option {
940 InteractionTrackerClampingOption::Auto => clamp_vector(value, values.min_position, values.max_position),
941 InteractionTrackerClampingOption::Disabled => value,
942 };
943 self.update_values(|v| {
944 v.position = position;
945 v.position_velocity = Vector3::ZERO;
946 v.scale_velocity = 0.0;
947 });
948 self.values_changed(request_id);
949 self.enter_idle(request_id);
950 }
951
952 pub fn try_update_position_with_animation(&self, animation: impl Into<InteractionTrackerAnimation>) -> i32 {
956 let id = self.new_request_id();
957 if !self.ignore_while_interacting(id) {
958 let track = match animation.into() {
959 InteractionTrackerAnimation::Vector3KeyFrame(a) => Some(Track::KeyFrames(KeyFrameTrack::<Vector3>::new(&a))),
960 InteractionTrackerAnimation::Expression(a) => Self::expression_track::<Vector3>(a),
961 InteractionTrackerAnimation::ScalarKeyFrame(_) => None,
962 };
963 match track {
964 Some(track) => {
965 let starting = self.position();
966 self.start_custom(CustomRun {
967 request_id: id,
968 elapsed: 0.0,
969 kind: CustomKind::Position { track, starting },
970 });
971 }
972 None => self.notify(Notification::RequestIgnored(InteractionTrackerRequestIgnoredArgs { request_id: id })),
973 }
974 }
975 self.flush();
976 id
977 }
978
979 fn expression_track<T: TrackValue>(animation: Rc<ExpressionAnimation>) -> Option<Track<T>> {
980 let source = animation.expression()?;
981 let expression = Expression::parse(&source).ok()?;
982 Some(Track::Expression { expression: Rc::new(expression), animation })
983 }
984
985 pub fn try_update_position_with_additional_velocity(&self, velocity_in_pixels_per_second: Vector3) -> i32 {
989 let id = self.new_request_id();
990 if !self.ignore_while_interacting(id) {
991 let values = self.values.get();
992 let base = match self.state.get() {
993 InteractionTrackerState::Idle => Vector3::ZERO,
994 _ => values.position_velocity,
995 };
996 let scale_velocity = match self.state.get() {
997 InteractionTrackerState::Idle => 0.0,
998 _ => values.scale_velocity,
999 };
1000 self.enter_inertia(base + velocity_in_pixels_per_second, scale_velocity, Vector3::ZERO, id, true);
1001 }
1002 self.flush();
1003 id
1004 }
1005
1006 pub fn try_update_scale(&self, value: f32, center_point: Vector3) -> i32 {
1010 let id = self.new_request_id();
1011 if !self.ignore_while_interacting(id) {
1012 self.stop_runs();
1013 self.apply_scale(value, center_point);
1014 self.update_values(|v| {
1015 v.position_velocity = Vector3::ZERO;
1016 v.scale_velocity = 0.0;
1017 });
1018 self.values_changed(id);
1019 self.enter_idle(id);
1020 }
1021 self.flush();
1022 id
1023 }
1024
1025 fn apply_scale(&self, value: f32, center_point: Vector3) {
1028 let values = self.values.get();
1029 let new_scale = clamp_scalar(value, values.min_scale, values.max_scale);
1030 let old_scale = values.scale;
1031 let position = if old_scale != 0.0 {
1032 let c = Vector3::new(center_point.x, center_point.y, 0.0);
1033 clamp_vector((values.position + c) * (new_scale / old_scale) - c, values.min_position, values.max_position)
1034 } else {
1035 values.position
1036 };
1037 self.update_values(|v| {
1038 v.scale = new_scale;
1039 v.position = position;
1040 });
1041 }
1042
1043 pub fn try_update_scale_with_animation(&self, animation: impl Into<InteractionTrackerAnimation>, center_point: Vector3) -> i32 {
1048 let id = self.new_request_id();
1049 if !self.ignore_while_interacting(id) {
1050 let track = match animation.into() {
1051 InteractionTrackerAnimation::ScalarKeyFrame(a) => Some(Track::KeyFrames(KeyFrameTrack::<f32>::new(&a))),
1052 InteractionTrackerAnimation::Expression(a) => Self::expression_track::<f32>(a),
1053 InteractionTrackerAnimation::Vector3KeyFrame(_) => None,
1054 };
1055 match track {
1056 Some(track) => {
1057 let starting = self.scale();
1058 self.start_custom(CustomRun {
1059 request_id: id,
1060 elapsed: 0.0,
1061 kind: CustomKind::Scale { track, starting, center: center_point },
1062 });
1063 }
1064 None => self.notify(Notification::RequestIgnored(InteractionTrackerRequestIgnoredArgs { request_id: id })),
1065 }
1066 }
1067 self.flush();
1068 id
1069 }
1070
1071 pub fn try_update_scale_with_additional_velocity(&self, velocity_in_percent_per_second: f32, center_point: Vector3) -> i32 {
1075 let id = self.new_request_id();
1076 if !self.ignore_while_interacting(id) {
1077 let values = self.values.get();
1078 let (position_velocity, scale_velocity) = match self.state.get() {
1079 InteractionTrackerState::Idle => (Vector3::ZERO, 0.0),
1080 _ => (values.position_velocity, values.scale_velocity),
1081 };
1082 self.enter_inertia(position_velocity, scale_velocity + velocity_in_percent_per_second, center_point, id, true);
1083 }
1084 self.flush();
1085 id
1086 }
1087
1088 pub(crate) fn begin_interaction(&self) {
1093 self.active_interactions.set(self.active_interactions.get() + 1);
1094 if self.state.get() == InteractionTrackerState::Interacting {
1095 return;
1096 }
1097 self.stop_runs();
1098 self.raw_position.set(self.position());
1099 self.state.set(InteractionTrackerState::Interacting);
1100 self.update_values(|v| {
1101 v.is_position_rounding_suggested = false;
1102 v.position_velocity = Vector3::ZERO;
1103 v.scale_velocity = 0.0;
1104 });
1105 self.notify(Notification::Interacting(InteractionTrackerInteractingStateEnteredArgs {
1106 request_id: self.last_request_id.get(),
1107 is_from_binding: false,
1108 }));
1109 self.flush();
1110 }
1111
1112 pub(crate) fn interaction_delta(&self, delta: Vector3, velocity: Vector3) {
1115 if self.state.get() != InteractionTrackerState::Interacting {
1116 return;
1117 }
1118 let raw = self.raw_position.get() + delta;
1119 self.raw_position.set(raw);
1120 let values = self.values.get();
1121 let position = Vector3::new(
1122 rubber_band_scalar(raw.x, values.min_position.x, values.max_position.x),
1123 rubber_band_scalar(raw.y, values.min_position.y, values.max_position.y),
1124 rubber_band_scalar(raw.z, values.min_position.z, values.max_position.z),
1125 );
1126 self.update_values(|v| {
1127 v.position = position;
1128 v.position_velocity = velocity;
1129 });
1130 self.values_changed(self.last_request_id.get());
1131 self.flush();
1132 }
1133
1134 pub(crate) fn end_interaction(&self, velocity: Vector3) {
1138 let count = self.active_interactions.get().saturating_sub(1);
1139 self.active_interactions.set(count);
1140 if count > 0 || self.state.get() != InteractionTrackerState::Interacting {
1141 return;
1142 }
1143 self.enter_inertia(velocity, 0.0, Vector3::ZERO, self.last_request_id.get(), false);
1144 self.flush();
1145 }
1146
1147 pub(crate) fn wheel_delta(&self, delta: Vector3) {
1150 if self.state.get() == InteractionTrackerState::Interacting {
1151 return;
1152 }
1153 let values = self.values.get();
1154 let base = match (self.state.get(), &*self.inertia.borrow()) {
1155 (InteractionTrackerState::Inertia, Some(run)) => run.target_position,
1156 _ => values.position,
1157 };
1158 let target = clamp_vector(base + delta, values.min_position, values.max_position);
1159 let r = values.effective_position_decay_rate();
1160 let mut velocity = Vector3::ZERO;
1161 for i in 0..3 {
1162 let ri = retention(axis(r, i));
1163 let distance = axis(target, i) - axis(values.position, i);
1164 set_axis(&mut velocity, i, if ri > 0.0 { -distance * ri.ln() } else { 0.0 });
1166 }
1167 self.enter_inertia(velocity, values.scale_velocity, Vector3::ZERO, self.last_request_id.get(), false);
1168 self.flush();
1169 }
1170
1171 pub(crate) fn wheel_scale(&self, factor: f32, center_point: Vector3) {
1174 if self.state.get() == InteractionTrackerState::Interacting {
1175 return;
1176 }
1177 self.apply_scale(self.scale() * factor, center_point);
1178 self.values_changed(self.last_request_id.get());
1179 self.flush();
1180 }
1181
1182 fn stop_runs(&self) {
1185 drop(self.inertia.borrow_mut().take());
1186 drop(self.custom.borrow_mut().take());
1187 }
1188
1189 fn enter_idle(&self, request_id: i32) {
1190 self.stop_runs();
1191 self.update_values(|v| {
1192 v.position_velocity = Vector3::ZERO;
1193 v.scale_velocity = 0.0;
1194 v.is_position_rounding_suggested = true;
1195 });
1196 if self.state.get() == InteractionTrackerState::Idle {
1197 return;
1198 }
1199 self.state.set(InteractionTrackerState::Idle);
1200 self.last_frame_time.set(None);
1201 self.notify(Notification::Idle(InteractionTrackerIdleStateEnteredArgs { request_id, is_from_binding: false }));
1202 }
1203
1204 fn start_custom(&self, run: CustomRun) {
1205 self.stop_runs();
1206 let request_id = run.request_id;
1207 *self.custom.borrow_mut() = Some(run);
1208 self.state.set(InteractionTrackerState::CustomAnimation);
1209 self.last_frame_time.set(None);
1210 self.update_values(|v| v.is_position_rounding_suggested = false);
1211 self.notify(Notification::CustomAnimation(InteractionTrackerCustomAnimationStateEnteredArgs {
1212 request_id,
1213 is_from_binding: false,
1214 }));
1215 self.request_frame();
1216 }
1217
1218 fn matching_modifier(
1220 modifiers: &[InteractionTrackerInertiaModifier],
1221 values: TrackerValues,
1222 ) -> Option<InteractionTrackerInertiaModifier> {
1223 modifiers
1224 .iter()
1225 .find(|modifier| {
1226 modifier.condition().is_some_and(|condition| {
1227 evaluate_animation(&condition, values, ExpressionVariant::Invalid) == ExpressionVariant::Boolean(true)
1228 })
1229 })
1230 .cloned()
1231 }
1232
1233 fn enter_inertia(&self, velocity: Vector3, scale_velocity: f32, scale_center: Vector3, request_id: i32, from_impulse: bool) {
1236 self.stop_runs();
1237 let values = self.values.get();
1238 let decay = values.effective_position_decay_rate();
1239 let position_retention = Vector3::new(retention(decay.x), retention(decay.y), retention(decay.z));
1240 let scale_retention = retention(values.effective_scale_decay_rate());
1241 let natural_position = Vector3::new(
1242 natural_resting_value(values.position.x, velocity.x, position_retention.x),
1243 natural_resting_value(values.position.y, velocity.y, position_retention.y),
1244 natural_resting_value(values.position.z, velocity.z, position_retention.z),
1245 );
1246 let natural_scale = natural_resting_value(values.scale, scale_velocity, scale_retention);
1247 self.update_values(|v| {
1248 v.position_velocity = velocity;
1249 v.scale_velocity = scale_velocity;
1250 v.natural_resting_position = natural_position;
1251 v.natural_resting_scale = natural_scale;
1252 v.is_inertia_from_impulse = from_impulse;
1253 v.is_position_rounding_suggested = false;
1254 });
1255 let values = self.values.get();
1256
1257 let mut target_position = natural_position;
1259 let mut target_scale = natural_scale;
1260 let mut motions: [Option<Rc<ExpressionAnimation>>; 3] = [None, None, None];
1261 let lists = [
1262 self.position_x_modifiers.borrow().clone(),
1263 self.position_y_modifiers.borrow().clone(),
1264 self.scale_modifiers.borrow().clone(),
1265 ];
1266 for (index, modifiers) in lists.iter().enumerate() {
1267 match Self::matching_modifier(modifiers, values) {
1268 Some(InteractionTrackerInertiaModifier::RestingValue(modifier)) => {
1269 let resting = modifier
1270 .resting_value()
1271 .map(|expression| evaluate_animation(&expression, values, ExpressionVariant::Invalid))
1272 .and_then(|value| value.try_cast::<f32>());
1273 if let Some(resting) = resting {
1274 if index < 2 {
1275 set_axis(&mut target_position, index, resting);
1276 } else {
1277 target_scale = resting;
1278 }
1279 }
1280 }
1281 Some(InteractionTrackerInertiaModifier::Motion(modifier)) => motions[index] = modifier.motion(),
1282 None => {}
1283 }
1284 }
1285 let target_position = clamp_vector(target_position, values.min_position, values.max_position);
1286 let target_scale = clamp_scalar(target_scale, values.min_scale, values.max_scale);
1287
1288 *self.inertia.borrow_mut() = Some(InertiaRun {
1289 request_id,
1290 elapsed: 0.0,
1291 start_position: values.position,
1292 target_position,
1293 position_retention,
1294 start_scale: values.scale,
1295 target_scale,
1296 scale_retention,
1297 scale_center,
1298 motions,
1299 });
1300 self.state.set(InteractionTrackerState::Inertia);
1301 self.last_frame_time.set(None);
1302 self.notify(Notification::Inertia(InteractionTrackerInertiaStateEnteredArgs {
1303 modified_resting_position: Some(target_position),
1304 modified_resting_scale: Some(target_scale),
1305 natural_resting_position: natural_position,
1306 natural_resting_scale: natural_scale,
1307 position_velocity_in_pixels_per_second: velocity,
1308 scale_velocity_in_percent_per_second: scale_velocity,
1309 request_id,
1310 is_inertia_from_impulse: from_impulse,
1311 is_from_binding: false,
1312 }));
1313 self.request_frame();
1314 }
1315
1316 #[cfg(test)]
1321 pub(crate) fn set_frame_requests_enabled(&self, value: bool) {
1322 self.frame_requests_enabled.set(value);
1323 }
1324
1325 fn request_frame(&self) {
1326 if self.frame_requested.get() || !self.frame_requests_enabled.get() {
1327 return;
1328 }
1329 if !matches!(self.state.get(), InteractionTrackerState::Inertia | InteractionTrackerState::CustomAnimation) {
1330 return;
1331 }
1332 self.frame_requested.set(true);
1333 let weak = self.this.clone();
1334 MediaContext::instance().request_animation_frame(move |now| {
1335 if let Some(this) = weak.upgrade() {
1336 this.on_animation_frame(now);
1337 }
1338 });
1339 }
1340
1341 fn on_animation_frame(&self, now: TimeSpan) {
1342 self.frame_requested.set(false);
1343 let now = now.total_seconds();
1344 let elapsed = match self.last_frame_time.replace(Some(now)) {
1345 Some(last) => (now - last).max(0.0),
1346 None => 0.0,
1347 };
1348 self.advance(Duration::from_secs_f64(elapsed));
1349 self.request_frame();
1350 }
1351
1352 pub(crate) fn advance(&self, elapsed: Duration) {
1355 let dt = elapsed.as_secs_f32();
1356 match self.state.get() {
1357 InteractionTrackerState::Inertia => self.advance_inertia(dt),
1358 InteractionTrackerState::CustomAnimation => self.advance_custom(dt),
1359 _ => {}
1360 }
1361 self.flush();
1362 }
1363
1364 fn advance_inertia(&self, dt: f32) {
1365 let Some(mut run) = self.inertia.borrow_mut().take() else { return };
1366 run.elapsed += dt;
1367 let t = run.elapsed;
1368 let values = self.values.get();
1369
1370 let (scale, scale_velocity, scale_done) = match &run.motions[2] {
1372 Some(motion) => {
1373 let value = evaluate_animation(motion, values, values.scale.into()).try_cast::<f32>().unwrap_or(values.scale);
1374 let velocity = if dt > 0.0 { (value - values.scale) / dt } else { 0.0 };
1375 (value, velocity, dt > 0.0 && (value - values.scale).abs() < REST_EPSILON * 0.01)
1376 }
1377 None => exponential_motion(run.start_scale, run.target_scale, run.scale_retention, t),
1378 };
1379 if values.scale != 0.0 && scale != values.scale {
1380 let k = scale / values.scale;
1381 let c = Vector3::new(run.scale_center.x, run.scale_center.y, 0.0);
1382 run.start_position = (run.start_position + c) * k - c;
1383 run.target_position = (run.target_position + c) * k - c;
1384 }
1385
1386 let mut position = values.position;
1387 let mut velocity = Vector3::ZERO;
1388 let mut done = scale_done;
1389 for i in 0..3 {
1390 let motion = if i < 2 { run.motions[i].clone() } else { None };
1392 let (value, v, axis_done) = match &motion {
1393 Some(motion) => {
1394 let current = axis(values.position, i);
1395 let value = evaluate_animation(motion, values, current.into()).try_cast::<f32>().unwrap_or(current);
1396 let v = if dt > 0.0 { (value - current) / dt } else { 0.0 };
1397 (value, v, dt > 0.0 && (value - current).abs() < REST_EPSILON)
1398 }
1399 None => exponential_motion(
1400 axis(run.start_position, i),
1401 axis(run.target_position, i),
1402 axis(run.position_retention, i),
1403 t,
1404 ),
1405 };
1406 set_axis(&mut position, i, value);
1407 set_axis(&mut velocity, i, v);
1408 done &= axis_done;
1409 }
1410
1411 if done {
1412 let final_position = {
1413 let mut p = run.target_position;
1414 for i in 0..2 {
1415 if run.motions[i].is_some() {
1416 set_axis(&mut p, i, axis(position, i));
1417 }
1418 }
1419 clamp_vector(p, values.min_position, values.max_position)
1420 };
1421 let final_scale = if run.motions[2].is_some() {
1422 clamp_scalar(scale, values.min_scale, values.max_scale)
1423 } else {
1424 run.target_scale
1425 };
1426 self.update_values(|v| {
1427 v.position = final_position;
1428 v.scale = final_scale;
1429 });
1430 self.values_changed(run.request_id);
1431 self.enter_idle(run.request_id);
1432 } else {
1433 let request_id = run.request_id;
1434 *self.inertia.borrow_mut() = Some(run);
1435 self.update_values(|v| {
1436 v.position = position;
1437 v.position_velocity = velocity;
1438 v.scale = scale;
1439 v.scale_velocity = scale_velocity;
1440 });
1441 self.values_changed(request_id);
1442 }
1443 }
1444
1445 fn advance_custom(&self, dt: f32) {
1446 let Some(mut run) = self.custom.borrow_mut().take() else { return };
1447 run.elapsed += dt;
1448 let values = self.values.get();
1449 let finished = match &run.kind {
1450 CustomKind::Position { track, starting } => {
1451 let (value, finished) = track.value_at(run.elapsed, *starting, values.position, values);
1452 let velocity = if dt > 0.0 { (value - values.position) / dt } else { values.position_velocity };
1453 self.update_values(|v| {
1454 v.position = value;
1455 v.position_velocity = velocity;
1456 });
1457 finished
1458 }
1459 CustomKind::Scale { track, starting, center } => {
1460 let (value, finished) = track.value_at(run.elapsed, *starting, values.scale, values);
1461 let velocity = if dt > 0.0 { (value - values.scale) / dt } else { values.scale_velocity };
1462 self.apply_scale(value, *center);
1465 self.update_values(|v| v.scale_velocity = velocity);
1466 finished
1467 }
1468 };
1469 let request_id = run.request_id;
1470 self.values_changed(request_id);
1471 if !finished {
1472 *self.custom.borrow_mut() = Some(run);
1473 return;
1474 }
1475 let values = self.values.get();
1478 if clamp_vector(values.position, values.min_position, values.max_position) != values.position {
1479 self.enter_inertia(Vector3::ZERO, 0.0, Vector3::ZERO, request_id, false);
1480 } else {
1481 self.enter_idle(request_id);
1482 }
1483 }
1484
1485 fn values_changed(&self, request_id: i32) {
1488 let values = self.values.get();
1489 self.notify(Notification::ValuesChanged(InteractionTrackerValuesChangedArgs {
1490 position: values.position,
1491 scale: values.scale,
1492 request_id,
1493 }));
1494 }
1495
1496 fn notify(&self, notification: Notification) {
1497 if self.owner.is_some() {
1498 self.notifications.borrow_mut().push_back(notification);
1499 }
1500 }
1501
1502 fn flush(&self) {
1505 if self.notifying.get() {
1506 return;
1507 }
1508 let (Some(owner), Some(this)) = (self.owner.clone(), self.this.upgrade()) else {
1509 self.notifications.borrow_mut().clear();
1510 return;
1511 };
1512 self.notifying.set(true);
1513 loop {
1514 let next = self.notifications.borrow_mut().pop_front();
1515 let Some(next) = next else { break };
1516 match next {
1517 Notification::CustomAnimation(args) => owner.custom_animation_state_entered(&this, &args),
1518 Notification::Idle(args) => owner.idle_state_entered(&this, &args),
1519 Notification::Inertia(args) => owner.inertia_state_entered(&this, &args),
1520 Notification::Interacting(args) => owner.interacting_state_entered(&this, &args),
1521 Notification::RequestIgnored(args) => owner.request_ignored(&this, &args),
1522 Notification::ValuesChanged(args) => owner.values_changed(&this, &args),
1523 }
1524 }
1525 self.notifying.set(false);
1526 }
1527}
1528
1529impl AsCompositionObject for InteractionTracker {
1530 fn as_composition_object(&self) -> &CompositionObject {
1533 self.properties.as_composition_object()
1534 }
1535
1536 fn composition_type_name(&self) -> &'static str {
1537 "InteractionTracker"
1538 }
1539
1540 fn as_property_set(self: Rc<Self>) -> Option<Rc<CompositionPropertySet>> {
1541 Some(self.properties.clone())
1542 }
1543}