use super::{
render_shapes, CompositionProperty, IServerObject, IServerRenderResourceObserver, IServerVisualContent,
ServerCompositionShapeCollection, ServerCompositionViewBox, ServerCompositionVisual, ServerSizeDependantVisual,
ServerVisualRenderContext, ViewBoxMapping,
};
use crate::platform::LtrbRect;
use crate::rendering::composition::generated::{
ServerCompositionContainerVisualProps, ServerShapeVisualHooks, ServerShapeVisualProps,
};
use crate::rendering::composition::transport::BatchStreamReader;
use crate::{Matrix, Rect};
use std::any::{Any, TypeId};
use std::rc::Rc;
use std::time::Duration;
#[derive(Default)]
pub struct ServerShapeVisual {
props: ServerShapeVisualProps,
}
impl ServerShapeVisual {
pub fn new() -> Self {
Self::default()
}
pub fn props(&self) -> &ServerShapeVisualProps {
&self.props
}
pub fn shapes(&self) -> Vec<Rc<dyn IServerObject>> {
self.props
.shapes()
.as_ref()
.and_then(ServerCompositionShapeCollection::of)
.map(ServerCompositionShapeCollection::shapes)
.unwrap_or_default()
}
pub fn view_box_mapping(&self, size: crate::Vector) -> Option<ViewBoxMapping> {
ServerCompositionViewBox::mapping_of(&self.props.view_box()?, size)
}
fn observe(visual: &ServerCompositionVisual, resource: Option<Rc<dyn IServerObject>>, observe: bool) {
let Some(resource) = resource.and_then(|resource| resource.as_render_resource()) else { return };
let Some(this) = visual.weak().upgrade() else { return };
let observer: Rc<dyn IServerRenderResourceObserver> = this;
if observe {
resource.add_observer(&observer);
} else {
resource.remove_observer(&observer);
}
}
}
impl ServerShapeVisualHooks for ServerCompositionVisual {
fn base_deserialize_changes_core(&self, reader: &mut BatchStreamReader<'_>, committed_at: Duration) {
ServerCompositionVisual::base_deserialize_changes_core(self, reader, committed_at);
}
fn on_shapes_changing(&self) {
if let Some(content) = self.content_as::<ServerShapeVisual>() {
ServerShapeVisual::observe(self, content.props.shapes(), false);
}
}
fn on_shapes_changed(&self) {
if let Some(content) = self.content_as::<ServerShapeVisual>() {
ServerShapeVisual::observe(self, content.props.shapes(), true);
}
self.invalidate_content();
}
fn on_view_box_changing(&self) {
if let Some(content) = self.content_as::<ServerShapeVisual>() {
ServerShapeVisual::observe(self, content.props.view_box(), false);
}
}
fn on_view_box_changed(&self) {
if let Some(content) = self.content_as::<ServerShapeVisual>() {
ServerShapeVisual::observe(self, content.props.view_box(), true);
}
self.invalidate_content();
}
}
impl IServerVisualContent for ServerShapeVisual {
fn deserialize_changes_core(
&self,
visual: &ServerCompositionVisual,
reader: &mut BatchStreamReader<'_>,
committed_at: Duration,
) {
self.props.deserialize_changes_core(visual, reader, committed_at);
}
fn compute_own_content_bounds(&self, visual: &ServerCompositionVisual) -> Option<LtrbRect> {
ServerSizeDependantVisual::compute_own_content_bounds(visual)
}
fn size_changed(&self, visual: &ServerCompositionVisual) {
ServerSizeDependantVisual::size_changed(visual);
}
fn render_core(
&self,
visual: &ServerCompositionVisual,
context: &mut ServerVisualRenderContext<'_>,
_current_transformed_clip: LtrbRect,
) {
let size = visual.size();
if size.x <= 0.0 || size.y <= 0.0 {
return;
}
let shapes = self.shapes();
if shapes.is_empty() {
return;
}
let mapping = self.view_box_mapping(size);
let canvas = context.canvas();
let base = canvas.transform();
canvas.push_clip(Rect::new(0.0, 0.0, size.x, size.y));
if let Some(mapping) = &mapping {
canvas.push_clip(mapping.clip);
}
let parent = mapping.map_or(Matrix::IDENTITY, |mapping| mapping.transform);
render_shapes(canvas, &shapes, parent, base);
canvas.set_transform(base);
if mapping.is_some() {
canvas.pop_clip();
}
canvas.pop_clip();
}
fn dependency_queued_invalidate(&self, visual: &ServerCompositionVisual) {
visual.invalidate_content();
}
fn dispose(&self, visual: &ServerCompositionVisual) {
ServerShapeVisual::observe(visual, self.props.shapes(), false);
ServerShapeVisual::observe(visual, self.props.view_box(), false);
}
fn find_props(&self, type_id: TypeId) -> Option<&dyn Any> {
self.props.find_props(type_id)
}
fn get_composition_property(&self, name: &str) -> Option<&'static CompositionProperty> {
ServerCompositionContainerVisualProps::get_composition_property(name)
}
fn as_any(&self) -> &dyn Any {
self
}
}