use super::composition_object_impls::composition_object_impls;
use super::generated::{
AmbientLightHooks, AmbientLightProps, CompositionLightHooks, CompositionLightProps, DistantLightHooks,
DistantLightProps, PointLightHooks, PointLightProps, ServerAmbientLightProps, ServerDistantLightProps,
ServerPointLightProps, ServerSpotLightProps, SpotLightHooks, SpotLightProps, VisualUnorderedCollectionHooks,
VisualUnorderedCollectionProps,
};
use super::server::{
IServerObject, ServerAmbientLight, ServerCompositor, ServerDistantLight, ServerObjectId, ServerPointLight,
ServerSpotLight, ServerVisualUnorderedCollection,
};
use super::{CompositionObject, CompositionVisual, Compositor, ICompositionObject};
use crate::media::Color;
use crate::Vector3D;
use std::cell::RefCell;
use std::ops::Deref;
use std::rc::{Rc, Weak};
pub struct VisualUnorderedCollection {
this: Weak<VisualUnorderedCollection>,
object: CompositionObject,
props: VisualUnorderedCollectionProps<CompositionVisual>,
}
impl VisualUnorderedCollection {
pub(crate) fn new(compositor: &Rc<Compositor>) -> Rc<VisualUnorderedCollection> {
let server =
compositor.create_server_object(|compositor, _| ServerVisualUnorderedCollection::new(compositor));
let collection = Rc::new_cyclic(|this: &Weak<VisualUnorderedCollection>| VisualUnorderedCollection {
this: this.clone(),
object: CompositionObject::new(compositor, Some(server)),
props: VisualUnorderedCollectionProps::new(),
});
collection.props.initialize_defaults(&*collection);
collection
}
pub fn server(&self) -> ServerObjectId {
self.object.required_server()
}
pub fn items(&self) -> Vec<Rc<CompositionVisual>> {
self.props.items()
}
pub fn add(&self, visual: Rc<CompositionVisual>) {
if !self.props.contains(&visual) {
self.props.add(self, visual);
}
}
pub fn remove(&self, visual: &Rc<CompositionVisual>) -> bool {
self.props.remove(self, visual)
}
pub fn remove_all(&self) {
self.props.clear(self);
}
pub fn count(&self) -> usize {
self.props.count()
}
pub fn contains(&self, visual: &Rc<CompositionVisual>) -> bool {
self.props.contains(visual)
}
pub fn is_disposed(&self) -> bool {
self.object.is_disposed()
}
pub fn dispose(&self) {
self.object.dispose();
}
}
impl VisualUnorderedCollectionHooks<CompositionVisual> for VisualUnorderedCollection {}
composition_object_impls!(
VisualUnorderedCollection, "VisualUnorderedCollection",
serialize: |this, writer| this.props.serialize_changes_core(this, writer),
start_animation: |_this, _name, _animation, _final_value| false,
composition_property: |_this, _name| None,
);
pub(crate) enum CompositionLightKind {
Ambient(AmbientLightProps),
Distant(DistantLightProps),
Point(PointLightProps),
Spot(SpotLightProps),
}
pub struct CompositionLight {
this: Weak<CompositionLight>,
object: CompositionObject,
kind: CompositionLightKind,
targets: Rc<VisualUnorderedCollection>,
exclusions: Rc<VisualUnorderedCollection>,
xaml_binding: RefCell<(Option<String>, Option<Rc<CompositionVisual>>, bool)>,
}
impl CompositionLight {
fn create(
compositor: &Rc<Compositor>,
kind: CompositionLightKind,
server: impl FnOnce(&Rc<ServerCompositor>) -> Rc<dyn IServerObject> + Send + 'static,
) -> Rc<CompositionLight> {
let targets = VisualUnorderedCollection::new(compositor);
let exclusions = VisualUnorderedCollection::new(compositor);
let server = compositor.create_server_object(move |compositor, _| server(compositor));
let light = Rc::new_cyclic(|this: &Weak<CompositionLight>| CompositionLight {
this: this.clone(),
object: CompositionObject::new(compositor, Some(server)),
kind,
targets,
exclusions,
xaml_binding: RefCell::new((None, None, false)),
});
match &light.kind {
CompositionLightKind::Ambient(props) => props.initialize_defaults(&*light),
CompositionLightKind::Distant(props) => props.initialize_defaults(&*light),
CompositionLightKind::Point(props) => props.initialize_defaults(&*light),
CompositionLightKind::Spot(props) => props.initialize_defaults(&*light),
}
let targets: Rc<dyn ICompositionObject> = light.targets.clone();
light.light_props().set_targets(&*light, Some(targets));
let exclusions: Rc<dyn ICompositionObject> = light.exclusions.clone();
light.light_props().set_exclusions_from_targets(&*light, Some(exclusions));
light
}
pub fn light_props(&self) -> &CompositionLightProps {
match &self.kind {
CompositionLightKind::Ambient(props) => props.base(),
CompositionLightKind::Distant(props) => props.base(),
CompositionLightKind::Point(props) => props.base(),
CompositionLightKind::Spot(props) => props.base(),
}
}
pub fn compositor(&self) -> &Rc<Compositor> {
self.object.compositor()
}
pub fn object(&self) -> &CompositionObject {
&self.object
}
pub fn server(&self) -> ServerObjectId {
self.object.required_server()
}
pub fn is_disposed(&self) -> bool {
self.object.is_disposed()
}
pub fn dispose(&self) {
self.object.dispose();
self.targets.dispose();
self.exclusions.dispose();
}
pub fn type_name(&self) -> &'static str {
match &self.kind {
CompositionLightKind::Ambient(_) => "AmbientLight",
CompositionLightKind::Distant(_) => "DistantLight",
CompositionLightKind::Point(_) => "PointLight",
CompositionLightKind::Spot(_) => "SpotLight",
}
}
pub fn targets(&self) -> &Rc<VisualUnorderedCollection> {
&self.targets
}
pub fn exclusions_from_targets(&self) -> &Rc<VisualUnorderedCollection> {
&self.exclusions
}
pub fn set_xaml_binding(&self, id: Option<String>, scope: Option<Rc<CompositionVisual>>) {
{
let mut binding = self.xaml_binding.borrow_mut();
let same_scope = match (&binding.1, &scope) {
(Some(a), Some(b)) => Rc::ptr_eq(a, b),
(None, None) => true,
_ => false,
};
if binding.0 == id && same_scope {
return;
}
*binding = (id, scope, true);
}
self.object
.register_for_serialization(|| self.this.upgrade().map(|this| this as Rc<dyn crate::rendering::composition::ICompositorSerializable>));
}
pub fn xaml_id(&self) -> Option<String> {
self.xaml_binding.borrow().0.clone()
}
fn serialize_xaml_binding(&self, writer: &mut crate::rendering::composition::transport::BatchStreamWriter<'_>) {
let mut binding = self.xaml_binding.borrow_mut();
let changes = binding.2.then(|| crate::rendering::composition::server::XamlLightChanges {
id: binding.0.clone(),
scope: binding.1.as_ref().and_then(|scope| scope.object().server()),
});
binding.2 = false;
writer.write_value(changes);
}
pub fn is_enabled(&self) -> bool {
self.light_props().is_enabled()
}
pub fn set_is_enabled(&self, value: bool) {
self.light_props().set_is_enabled(self, value)
}
}
impl CompositionLightHooks for CompositionLight {}
impl AmbientLightHooks for CompositionLight {}
impl DistantLightHooks for CompositionLight {}
impl PointLightHooks for CompositionLight {}
impl SpotLightHooks for CompositionLight {}
composition_object_impls!(
CompositionLight, "CompositionLight",
serialize: |this, writer| {
match &this.kind {
CompositionLightKind::Ambient(props) => props.serialize_changes_core(this, writer),
CompositionLightKind::Distant(props) => props.serialize_changes_core(this, writer),
CompositionLightKind::Point(props) => props.serialize_changes_core(this, writer),
CompositionLightKind::Spot(props) => props.serialize_changes_core(this, writer),
}
this.serialize_xaml_binding(writer);
},
start_animation: |this, name, animation, final_value| match &this.kind {
CompositionLightKind::Ambient(props) => props.start_animation(this, name, animation, final_value),
CompositionLightKind::Distant(props) => props.start_animation(this, name, animation, final_value),
CompositionLightKind::Point(props) => props.start_animation(this, name, animation, final_value),
CompositionLightKind::Spot(props) => props.start_animation(this, name, animation, final_value),
},
composition_property: |this, name| match &this.kind {
CompositionLightKind::Ambient(_) => ServerAmbientLightProps::get_composition_property(name),
CompositionLightKind::Distant(_) => ServerDistantLightProps::get_composition_property(name),
CompositionLightKind::Point(_) => ServerPointLightProps::get_composition_property(name),
CompositionLightKind::Spot(_) => ServerSpotLightProps::get_composition_property(name),
},
);
fn as_visual(object: Option<Rc<dyn ICompositionObject>>) -> Option<Rc<CompositionVisual>> {
object.and_then(|visual| visual.into_any_rc().downcast::<CompositionVisual>().ok())
}
fn as_object(visual: Option<Rc<CompositionVisual>>) -> Option<Rc<dyn ICompositionObject>> {
visual.map(|visual| visual as Rc<dyn ICompositionObject>)
}
macro_rules! light_handle {
($(#[$meta:meta])* $name:ident, $kind:ident, $props:ident) => {
$(#[$meta])*
#[derive(Clone)]
pub struct $name(Rc<CompositionLight>);
impl Deref for $name {
type Target = Rc<CompositionLight>;
fn deref(&self) -> &Rc<CompositionLight> {
&self.0
}
}
impl $name {
pub fn from_light(light: &Rc<CompositionLight>) -> Option<$name> {
matches!(light.kind, CompositionLightKind::$kind(_)).then(|| $name(light.clone()))
}
pub fn props(&self) -> &$props {
match &self.0.kind {
CompositionLightKind::$kind(props) => props,
_ => unreachable!("a light handle wraps a light of its class"),
}
}
}
};
}
light_handle!(
AmbientLight,
Ambient,
AmbientLightProps
);
impl AmbientLight {
pub(crate) fn new(compositor: &Rc<Compositor>) -> AmbientLight {
AmbientLight(CompositionLight::create(
compositor,
CompositionLightKind::Ambient(AmbientLightProps::new()),
|c| ServerAmbientLight::new(c),
))
}
pub fn color(&self) -> Color {
self.props().color()
}
pub fn set_color(&self, value: Color) {
self.props().set_color(&*self.0, value)
}
pub fn intensity(&self) -> f32 {
self.props().intensity()
}
pub fn set_intensity(&self, value: f32) {
self.props().set_intensity(&*self.0, value)
}
}
light_handle!(
DistantLight,
Distant,
DistantLightProps
);
impl DistantLight {
pub(crate) fn new(compositor: &Rc<Compositor>) -> DistantLight {
DistantLight(CompositionLight::create(
compositor,
CompositionLightKind::Distant(DistantLightProps::new()),
|c| ServerDistantLight::new(c),
))
}
pub fn color(&self) -> Color {
self.props().color()
}
pub fn set_color(&self, value: Color) {
self.props().set_color(&*self.0, value)
}
pub fn coordinate_space(&self) -> Option<Rc<CompositionVisual>> {
as_visual(self.props().coordinate_space())
}
pub fn set_coordinate_space(&self, value: Option<Rc<CompositionVisual>>) {
self.props().set_coordinate_space(&*self.0, as_object(value))
}
pub fn direction(&self) -> Vector3D {
self.props().direction()
}
pub fn set_direction(&self, value: Vector3D) {
self.props().set_direction(&*self.0, value)
}
pub fn intensity(&self) -> f32 {
self.props().intensity()
}
pub fn set_intensity(&self, value: f32) {
self.props().set_intensity(&*self.0, value)
}
}
light_handle!(
PointLight,
Point,
PointLightProps
);
impl PointLight {
pub(crate) fn new(compositor: &Rc<Compositor>) -> PointLight {
PointLight(CompositionLight::create(
compositor,
CompositionLightKind::Point(PointLightProps::new()),
|c| ServerPointLight::new(c),
))
}
pub fn color(&self) -> Color {
self.props().color()
}
pub fn set_color(&self, value: Color) {
self.props().set_color(&*self.0, value)
}
pub fn constant_attenuation(&self) -> f32 {
self.props().constant_attenuation()
}
pub fn set_constant_attenuation(&self, value: f32) {
self.props().set_constant_attenuation(&*self.0, value)
}
pub fn linear_attenuation(&self) -> f32 {
self.props().linear_attenuation()
}
pub fn set_linear_attenuation(&self, value: f32) {
self.props().set_linear_attenuation(&*self.0, value)
}
pub fn quadratic_attenuation(&self) -> f32 {
self.props().quadratic_attenuation()
}
pub fn set_quadratic_attenuation(&self, value: f32) {
self.props().set_quadratic_attenuation(&*self.0, value)
}
pub fn coordinate_space(&self) -> Option<Rc<CompositionVisual>> {
as_visual(self.props().coordinate_space())
}
pub fn set_coordinate_space(&self, value: Option<Rc<CompositionVisual>>) {
self.props().set_coordinate_space(&*self.0, as_object(value))
}
pub fn offset(&self) -> Vector3D {
self.props().offset()
}
pub fn set_offset(&self, value: Vector3D) {
self.props().set_offset(&*self.0, value)
}
pub fn intensity(&self) -> f32 {
self.props().intensity()
}
pub fn set_intensity(&self, value: f32) {
self.props().set_intensity(&*self.0, value)
}
pub fn min_attenuation_cutoff(&self) -> f32 {
self.props().min_attenuation_cutoff()
}
pub fn set_min_attenuation_cutoff(&self, value: f32) {
self.props().set_min_attenuation_cutoff(&*self.0, value)
}
pub fn max_attenuation_cutoff(&self) -> f32 {
self.props().max_attenuation_cutoff()
}
pub fn set_max_attenuation_cutoff(&self, value: f32) {
self.props().set_max_attenuation_cutoff(&*self.0, value)
}
}
light_handle!(
SpotLight,
Spot,
SpotLightProps
);
impl SpotLight {
pub(crate) fn new(compositor: &Rc<Compositor>) -> SpotLight {
SpotLight(CompositionLight::create(
compositor,
CompositionLightKind::Spot(SpotLightProps::new()),
|c| ServerSpotLight::new(c),
))
}
pub fn constant_attenuation(&self) -> f32 {
self.props().constant_attenuation()
}
pub fn set_constant_attenuation(&self, value: f32) {
self.props().set_constant_attenuation(&*self.0, value)
}
pub fn linear_attenuation(&self) -> f32 {
self.props().linear_attenuation()
}
pub fn set_linear_attenuation(&self, value: f32) {
self.props().set_linear_attenuation(&*self.0, value)
}
pub fn quadratic_attenuation(&self) -> f32 {
self.props().quadratic_attenuation()
}
pub fn set_quadratic_attenuation(&self, value: f32) {
self.props().set_quadratic_attenuation(&*self.0, value)
}
pub fn coordinate_space(&self) -> Option<Rc<CompositionVisual>> {
as_visual(self.props().coordinate_space())
}
pub fn set_coordinate_space(&self, value: Option<Rc<CompositionVisual>>) {
self.props().set_coordinate_space(&*self.0, as_object(value))
}
pub fn direction(&self) -> Vector3D {
self.props().direction()
}
pub fn set_direction(&self, value: Vector3D) {
self.props().set_direction(&*self.0, value)
}
pub fn inner_cone_angle(&self) -> f32 {
self.props().inner_cone_angle()
}
pub fn set_inner_cone_angle(&self, value: f32) {
self.props().set_inner_cone_angle(&*self.0, value)
}
pub fn inner_cone_angle_in_degrees(&self) -> f32 {
self.inner_cone_angle().to_degrees()
}
pub fn set_inner_cone_angle_in_degrees(&self, value: f32) {
self.set_inner_cone_angle(value.to_radians())
}
pub fn inner_cone_color(&self) -> Color {
self.props().inner_cone_color()
}
pub fn set_inner_cone_color(&self, value: Color) {
self.props().set_inner_cone_color(&*self.0, value)
}
pub fn inner_cone_intensity(&self) -> f32 {
self.props().inner_cone_intensity()
}
pub fn set_inner_cone_intensity(&self, value: f32) {
self.props().set_inner_cone_intensity(&*self.0, value)
}
pub fn outer_cone_angle(&self) -> f32 {
self.props().outer_cone_angle()
}
pub fn set_outer_cone_angle(&self, value: f32) {
self.props().set_outer_cone_angle(&*self.0, value)
}
pub fn outer_cone_angle_in_degrees(&self) -> f32 {
self.outer_cone_angle().to_degrees()
}
pub fn set_outer_cone_angle_in_degrees(&self, value: f32) {
self.set_outer_cone_angle(value.to_radians())
}
pub fn outer_cone_color(&self) -> Color {
self.props().outer_cone_color()
}
pub fn set_outer_cone_color(&self, value: Color) {
self.props().set_outer_cone_color(&*self.0, value)
}
pub fn outer_cone_intensity(&self) -> f32 {
self.props().outer_cone_intensity()
}
pub fn set_outer_cone_intensity(&self, value: f32) {
self.props().set_outer_cone_intensity(&*self.0, value)
}
pub fn offset(&self) -> Vector3D {
self.props().offset()
}
pub fn set_offset(&self, value: Vector3D) {
self.props().set_offset(&*self.0, value)
}
pub fn min_attenuation_cutoff(&self) -> f32 {
self.props().min_attenuation_cutoff()
}
pub fn set_min_attenuation_cutoff(&self, value: f32) {
self.props().set_min_attenuation_cutoff(&*self.0, value)
}
pub fn max_attenuation_cutoff(&self) -> f32 {
self.props().max_attenuation_cutoff()
}
pub fn set_max_attenuation_cutoff(&self, value: f32) {
self.props().set_max_attenuation_cutoff(&*self.0, value)
}
}
impl Compositor {
pub fn create_ambient_light(&self) -> AmbientLight {
AmbientLight::new(&self.this_handle())
}
pub fn create_distant_light(&self) -> DistantLight {
DistantLight::new(&self.this_handle())
}
pub fn create_point_light(&self) -> PointLight {
PointLight::new(&self.this_handle())
}
pub fn create_spot_light(&self) -> SpotLight {
SpotLight::new(&self.this_handle())
}
}