Skip to main content

ferroui_base/rendering/composition/drawing/
server_composition_render_data.rs

1use super::{RenderDataStream, ReplayScope};
2use crate::platform::{IDrawingContextImpl, LtrbRect};
3use crate::rendering::composition::server::{
4    IServerObject, IServerRenderResource, IServerRenderResourceHost, IServerRenderResourceObserver, ServerCompositor,
5    ServerRenderResourceCore,
6};
7use crate::rendering::composition::transport::BatchStreamReader;
8use crate::Rect;
9use std::any::Any;
10use std::cell::{Cell, RefCell};
11use std::rc::{Rc, Weak};
12use std::time::Duration;
13
14/// What a visual drew, on the server: the stream to replay, and the
15/// subscriptions to the server-side resources it draws with.
16pub struct ServerCompositionRenderData {
17    this: Weak<ServerCompositionRenderData>,
18    compositor: Weak<ServerCompositor>,
19    core: ServerRenderResourceCore,
20    stream: RefCell<Option<RenderDataStream>>,
21    referenced_resources: RefCell<Vec<Rc<dyn IServerRenderResource>>>,
22    bounds: Cell<Option<LtrbRect>>,
23    bounds_valid: Cell<bool>,
24    replay_scopes: RefCell<Vec<ReplayScope>>,
25}
26
27impl ServerCompositionRenderData {
28    pub fn new(compositor: &Rc<ServerCompositor>) -> Rc<ServerCompositionRenderData> {
29        Rc::new_cyclic(|this| ServerCompositionRenderData {
30            this: this.clone(),
31            compositor: Rc::downgrade(compositor),
32            core: ServerRenderResourceCore::new(),
33            stream: RefCell::new(None),
34            referenced_resources: RefCell::new(Vec::new()),
35            bounds: Cell::new(None),
36            bounds_valid: Cell::new(false),
37            replay_scopes: RefCell::new(Vec::new()),
38        })
39    }
40
41    pub fn is_disposed(&self) -> bool {
42        self.core.is_disposed()
43    }
44
45    fn as_observer(&self) -> Option<Rc<dyn IServerRenderResourceObserver>> {
46        self.this.upgrade().map(|this| this as Rc<dyn IServerRenderResourceObserver>)
47    }
48
49    /// The bounds of the drawn content, rounded outwards to whole units;
50    /// `None` if nothing is drawn.
51    pub fn bounds(&self) -> Option<LtrbRect> {
52        if !self.bounds_valid.get() {
53            self.bounds.set(self.calculate_render_bounds());
54            self.bounds_valid.set(true);
55        }
56        self.bounds.get()
57    }
58
59    fn calculate_render_bounds(&self) -> Option<LtrbRect> {
60        let bounds = self.stream.borrow().as_ref().and_then(RenderDataStream::calculate_bounds);
61        Self::apply_render_bounds_rounding(bounds.map(LtrbRect::from_rect))
62    }
63
64    /// Rounds render bounds outwards to whole units.
65    pub fn apply_render_bounds_rounding_rect(rect: Option<Rect>) -> Option<Rect> {
66        Self::apply_render_bounds_rounding(rect.map(LtrbRect::from_rect)).map(|r| r.to_rect())
67    }
68
69    /// Rounds render bounds outwards to whole units.
70    pub fn apply_render_bounds_rounding(rect: Option<LtrbRect>) -> Option<LtrbRect> {
71        rect.map(|r| LtrbRect { left: r.left.floor(), top: r.top.floor(), right: r.right.ceil(), bottom: r.bottom.ceil() })
72    }
73
74    /// Replays the drawn content onto a platform drawing context.
75    pub fn render(&self, context: &mut dyn IDrawingContextImpl) {
76        if let Some(stream) = self.stream.borrow().as_ref() {
77            stream.replay_with_scopes(context, &mut self.replay_scopes.borrow_mut());
78        }
79    }
80
81    fn reset(&self) {
82        self.bounds.set(None);
83        self.bounds_valid.set(false);
84        let referenced = std::mem::take(&mut *self.referenced_resources.borrow_mut());
85        if let Some(observer) = self.as_observer() {
86            for resource in &referenced {
87                resource.remove_observer(&observer);
88            }
89        }
90        if let Some(mut stream) = self.stream.borrow_mut().take() {
91            stream.dispose_resources();
92            stream.dispose();
93        }
94    }
95}
96
97impl IServerObject for ServerCompositionRenderData {
98    fn deserialize_changes_core(&self, reader: &mut BatchStreamReader<'_>, _committed_at: Duration) {
99        self.reset();
100        let mut stream = RenderDataStream::new();
101        let compositor = self.compositor.upgrade();
102        let server_ids = RefCell::new(Vec::new());
103        stream.deserialize_from(reader, &|id| {
104            server_ids.borrow_mut().push(id);
105            compositor.as_ref().and_then(|c| c.get_object(id))
106        });
107        let server_ids = server_ids.into_inner();
108
109        // Server-side brushes, pens and geometries are render resources:
110        // follow their changes.
111        if let (Some(compositor), Some(observer)) = (&compositor, self.as_observer()) {
112            let mut referenced = self.referenced_resources.borrow_mut();
113            for id in &server_ids {
114                if let Some(resource) = compositor.get_object(*id).and_then(|o| o.as_render_resource()) {
115                    resource.add_observer(&observer);
116                    referenced.push(resource);
117                }
118            }
119        }
120        *self.stream.borrow_mut() = Some(stream);
121    }
122
123    fn values_invalidated(&self) {
124        self.core.invalidated(self);
125    }
126
127    fn dispose(&self) {
128        self.reset();
129        self.core.dispose();
130    }
131
132    fn as_render_resource(self: Rc<Self>) -> Option<Rc<dyn IServerRenderResource>> {
133        Some(self)
134    }
135
136    fn as_any(&self) -> &dyn Any {
137        self
138    }
139
140    fn into_any_rc(self: Rc<Self>) -> Rc<dyn Any> {
141        self
142    }
143}
144
145impl IServerRenderResourceObserver for ServerCompositionRenderData {
146    fn dependency_queued_invalidate(&self, _sender: &dyn IServerRenderResource) {
147        self.bounds_valid.set(false);
148        self.core.dependency_queued_invalidate(self);
149    }
150}
151
152impl IServerRenderResource for ServerCompositionRenderData {
153    fn add_observer(&self, observer: &Rc<dyn IServerRenderResourceObserver>) {
154        self.core.add_observer(observer);
155    }
156
157    fn remove_observer(&self, observer: &Rc<dyn IServerRenderResourceObserver>) {
158        self.core.remove_observer(observer);
159    }
160
161    fn queued_invalidate(&self) {
162        self.core.queued_invalidate(self);
163    }
164}
165
166impl IServerRenderResourceHost for ServerCompositionRenderData {
167    fn compositor(&self) -> Option<Rc<ServerCompositor>> {
168        self.compositor.upgrade()
169    }
170
171    fn as_render_resource_rc(&self) -> Option<Rc<dyn IServerRenderResource>> {
172        self.this.upgrade().map(|this| this as Rc<dyn IServerRenderResource>)
173    }
174}