Skip to main content

ferroui_base/rendering/composition/server/server_composition_visual/
act.rs

1//! ATT = Ancestor Transform Tracker.
2//!
3//! While we generally avoid dealing with keeping world transforms up to
4//! date, we still need it for cases like adorners. Instead of updating
5//! world transforms eagerly, we use a subscription model where visuals can
6//! subscribe to notifications when any ancestor's world transform changes.
7
8use super::ServerCompositionVisual;
9use crate::Matrix;
10use std::cell::{Cell, RefCell};
11use std::rc::{Rc, Weak};
12
13/// A subscription to the transform changes of the ancestor chain of a
14/// visual: the two callbacks upstream stores as delegates.
15#[derive(Clone)]
16pub(super) enum ActSubscriber {
17    /// `ParentActSubscriptionAction` of a child: forwards to the
18    /// subscribers of that child.
19    CombinedTransformChanged(Weak<ServerCompositionVisual>),
20    /// `AdornedVisualActSubscriptionAction` of an adorner: enqueues the
21    /// adorner for an update.
22    AdornedVisualWorldTransformChanged(Weak<ServerCompositionVisual>),
23}
24
25impl ActSubscriber {
26    fn same(&self, other: &ActSubscriber) -> bool {
27        match (self, other) {
28            (ActSubscriber::CombinedTransformChanged(a), ActSubscriber::CombinedTransformChanged(b)) => {
29                Weak::ptr_eq(a, b)
30            }
31            (
32                ActSubscriber::AdornedVisualWorldTransformChanged(a),
33                ActSubscriber::AdornedVisualWorldTransformChanged(b),
34            ) => Weak::ptr_eq(a, b),
35            _ => false,
36        }
37    }
38
39    fn invoke(&self) {
40        match self {
41            ActSubscriber::CombinedTransformChanged(visual) => {
42                if let Some(visual) = visual.upgrade() {
43                    visual.att_helper_combined_transform_changed();
44                }
45            }
46            ActSubscriber::AdornedVisualWorldTransformChanged(visual) => {
47                if let Some(visual) = visual.upgrade() {
48                    visual.adorner_helper_enqueue_for_adorner_update();
49                }
50            }
51        }
52    }
53}
54
55#[derive(Default)]
56pub(super) struct AttHelper {
57    pub(super) ancestor_chain_transform_subscribers: RefCell<Vec<ActSubscriber>>,
58    // We keep adorner stuff here too
59    pub(super) enqueued_for_adorner_update: Cell<bool>,
60}
61
62impl ServerCompositionVisual {
63    fn parent_act_subscription_action(&self) -> ActSubscriber {
64        ActSubscriber::CombinedTransformChanged(self.this.clone())
65    }
66
67    pub(super) fn adorned_visual_act_subscription_action(&self) -> ActSubscriber {
68        ActSubscriber::AdornedVisualWorldTransformChanged(self.this.clone())
69    }
70
71    /// The number of subscribers to the transforms of the ancestor chain of
72    /// the visual: the adorners of the visual, and its children that have
73    /// subscribers of their own (a diagnostics member, used by tests to
74    /// verify that subscriptions are released).
75    pub fn ancestor_transform_subscriber_count(&self) -> usize {
76        self.att_helper.ancestor_chain_transform_subscribers.borrow().len()
77    }
78
79    pub(super) fn att_helper_combined_transform_changed(&self) {
80        if self.att_helper.ancestor_chain_transform_subscribers.borrow().is_empty() {
81            return;
82        }
83        let subscribers = self.att_helper.ancestor_chain_transform_subscribers.borrow().clone();
84        for subscriber in subscribers {
85            subscriber.invoke();
86        }
87    }
88
89    pub(super) fn att_helper_parent_changing(&self) {
90        if let Some(parent) = self.parent() {
91            if !self.att_helper.ancestor_chain_transform_subscribers.borrow().is_empty() {
92                parent.att_helper_unsubscribe_from_act_notification(&self.parent_act_subscription_action());
93            }
94        }
95    }
96
97    pub(super) fn att_helper_parent_changed(&self) {
98        let parent = self.parent();
99        if let Some(parent) = &parent {
100            if !self.att_helper.ancestor_chain_transform_subscribers.borrow().is_empty() {
101                parent.att_helper_subscribe_to_act_notification(self.parent_act_subscription_action());
102            }
103        }
104        if parent.is_some() && self.adorned_visual().is_some() {
105            self.adorner_helper_enqueue_for_adorner_update();
106        }
107    }
108
109    pub(super) fn att_helper_subscribe_to_act_notification(&self, cb: ActSubscriber) {
110        let count = {
111            let mut subscribers = self.att_helper.ancestor_chain_transform_subscribers.borrow_mut();
112            if !subscribers.iter().any(|s| s.same(&cb)) {
113                subscribers.push(cb);
114            }
115            subscribers.len()
116        };
117        if count == 1 {
118            if let Some(parent) = self.parent() {
119                parent.att_helper_subscribe_to_act_notification(self.parent_act_subscription_action());
120            }
121        }
122    }
123
124    pub(super) fn att_helper_unsubscribe_from_act_notification(&self, cb: &ActSubscriber) {
125        let count = {
126            let mut subscribers = self.att_helper.ancestor_chain_transform_subscribers.borrow_mut();
127            subscribers.retain(|s| !s.same(cb));
128            subscribers.len()
129        };
130        if count == 0 {
131            if let Some(parent) = self.parent() {
132                parent.att_helper_unsubscribe_from_act_notification(&self.parent_act_subscription_action());
133            }
134        }
135    }
136
137    /// The transform from `visual` to `ancestor`; `None` if `ancestor` is
138    /// not an ancestor of `visual`.
139    pub(super) fn compute_transform_from_ancestor(
140        visual: &Rc<ServerCompositionVisual>,
141        ancestor: &Rc<ServerCompositionVisual>,
142    ) -> Option<Matrix> {
143        let mut transform = visual.own_transform.get().unwrap_or(Matrix::IDENTITY);
144        let mut visual = visual.clone();
145        while let Some(parent) = visual.parent() {
146            visual = parent;
147            if Rc::ptr_eq(&visual, ancestor) {
148                // Walked up to ancestor
149                return Some(transform);
150            }
151            if let Some(own_transform) = visual.own_transform.get() {
152                transform = transform * own_transform;
153            }
154        }
155        // Visual is a part of a different subtree, this is not supported
156        None
157    }
158}