use super::{
CompositionCustomVisual, CompositionCustomVisualHandler, CompositionVisual, ICompositionCustomVisualHandler,
ICompositionObjectAnimations,
};
use crate::media::{BoxShadow, BoxShadows, DropShadowRecipe, ImmediateDrawingContext};
use crate::{Rect, RoundedRect, Vector};
use std::any::Any;
use std::cell::{Cell, RefCell};
use std::rc::Rc;
use std::sync::Arc;
#[derive(Clone, Debug, PartialEq)]
struct ShadowShape {
shadows: Vec<BoxShadow>,
radius_x: f64,
radius_y: f64,
}
impl ShadowShape {
fn of(recipe: &DropShadowRecipe) -> ShadowShape {
ShadowShape { shadows: recipe.box_shadows(), radius_x: recipe.radius_x, radius_y: recipe.radius_y }
}
}
pub struct ThemeShadowVisual {
visual: CompositionCustomVisual,
shape: RefCell<Option<ShadowShape>>,
size: Cell<Vector>,
}
impl ThemeShadowVisual {
pub fn new(caster: &Rc<CompositionVisual>, recipe: &DropShadowRecipe) -> ThemeShadowVisual {
let compositor = caster.compositor().clone();
let shape = ShadowShape::of(recipe);
let initial = shape.clone();
let visual = compositor.create_custom_visual(move || {
Rc::new(ThemeShadowHandler { base: CompositionCustomVisualHandler::default(), shape: RefCell::new(Some(initial)) })
as Rc<dyn ICompositionCustomVisualHandler>
});
visual.set_is_hit_test_visible(false);
visual.set_clip_to_bounds(false);
visual.set_parent_for_transform(Some(caster.clone()));
let opacity = compositor.create_expression_animation_with("caster.Opacity");
opacity.set_reference_parameter("caster", caster.clone());
visual.start_animation("Opacity", &*opacity);
ThemeShadowVisual { visual, shape: RefCell::new(Some(shape)), size: Cell::new(Vector::default()) }
}
pub fn visual(&self) -> &Rc<CompositionVisual> {
&self.visual
}
pub fn update(&self, recipe: &DropShadowRecipe, size: Vector, visible: bool) {
let visible = visible && recipe.is_visible() && size.x > 0.0 && size.y > 0.0;
self.visual.set_visible(visible);
if self.size.get() != size {
self.size.set(size);
self.visual.set_size(size);
}
let shape = ShadowShape::of(recipe);
if self.shape.borrow().as_ref() == Some(&shape) {
return;
}
*self.shape.borrow_mut() = Some(shape.clone());
self.visual.send_handler_message(Arc::new(shape));
}
pub fn box_shadows(&self) -> Vec<BoxShadow> {
self.shape.borrow().as_ref().map(|shape| shape.shadows.clone()).unwrap_or_default()
}
pub fn dispose(&self) {
self.visual.dispose();
}
}
#[derive(Default)]
struct ThemeShadowHandler {
base: CompositionCustomVisualHandler,
shape: RefCell<Option<ShadowShape>>,
}
impl ThemeShadowHandler {
fn caster_rect(&self) -> Option<RoundedRect> {
let size = self.base.effective_size();
if size.x <= 2.0 || size.y <= 2.0 {
return None;
}
let shape = self.shape.borrow();
let shape = shape.as_ref()?;
let rect = Rect::new(1.0, 1.0, size.x - 2.0, size.y - 2.0);
Some(RoundedRect::from_radius_xy(rect, shape.radius_x, shape.radius_y))
}
fn box_shadows(&self) -> Option<BoxShadows> {
let shape = self.shape.borrow();
let shadows = &shape.as_ref()?.shadows;
let (first, rest) = shadows.split_first()?;
Some(BoxShadows::with_rest(*first, rest))
}
}
impl ICompositionCustomVisualHandler for ThemeShadowHandler {
fn handler_base(&self) -> &CompositionCustomVisualHandler {
&self.base
}
fn on_message(&self, message: Arc<dyn Any + Send + Sync>) {
if let Some(shape) = message.downcast_ref::<ShadowShape>() {
*self.shape.borrow_mut() = Some(shape.clone());
self.base.invalidate();
}
}
fn on_render(&self, drawing_context: &mut ImmediateDrawingContext<'_>) {
let (Some(rect), Some(shadows)) = (self.caster_rect(), self.box_shadows()) else { return };
drawing_context.platform_impl().draw_rectangle(None, None, rect, &shadows);
}
fn get_render_bounds(&self) -> Rect {
let size = self.base.effective_size();
let caster = Rect::new(0.0, 0.0, size.x, size.y);
match (self.caster_rect(), self.box_shadows()) {
(Some(rect), Some(shadows)) => caster.union(shadows.transform_bounds(rect.rect)),
_ => caster,
}
}
}