use super::drawing::CompositionRenderData;
use super::generated::{
CompositionContainerVisualProps, CompositionExperimentalAcrylicVisualHooks,
CompositionExperimentalAcrylicVisualProps,
};
use super::server::{IServerVisualContent, ServerCompositionDrawListVisual, ServerCompositionExperimentalAcrylicVisual};
use super::transport::{BatchStreamWriter, IRegisterForSerialization};
use super::visual::CompositionVisualKind;
use super::{CompositionVisual, Compositor};
use crate::media::{Geometry, IntersectionResult};
use crate::{Point, Ref, Visual, WeakRef};
use std::cell::{Cell, RefCell};
use std::ops::Deref;
use std::rc::Rc;
pub trait ICompositionDrawListVisualExtension: 'static {
fn serialize_changes_core(&self, writer: &mut BatchStreamWriter<'_>);
}
pub(crate) struct DrawListData {
props: CompositionContainerVisualProps,
visual: Option<WeakRef<Visual>>,
draw_list_changed: Cell<bool>,
draw_list: RefCell<Option<Rc<CompositionRenderData>>>,
pub(super) acrylic: Option<CompositionExperimentalAcrylicVisualProps>,
extension: Option<Rc<dyn ICompositionDrawListVisualExtension>>,
}
impl DrawListData {
pub(super) fn props(&self) -> &CompositionContainerVisualProps {
&self.props
}
pub(super) fn initialize_defaults(&self, host: &CompositionVisual) {
self.props.initialize_defaults(host);
if let Some(acrylic) = &self.acrylic {
acrylic.initialize_own_defaults(host);
}
}
fn serialize_draw_list(&self, host: &CompositionVisual, writer: &mut BatchStreamWriter<'_>) {
writer.write(u8::from(self.draw_list_changed.get()));
if self.draw_list_changed.get() {
writer.write_server_object(self.draw_list.borrow().as_ref().map(|draw_list| draw_list.server()));
self.draw_list_changed.set(false);
}
self.props.serialize_changes_core(host, writer);
}
pub(super) fn serialize_changes_core(&self, host: &CompositionVisual, writer: &mut BatchStreamWriter<'_>) {
match &self.acrylic {
Some(acrylic) => acrylic.serialize_changes_core(host, writer),
None => {
self.serialize_draw_list(host, writer);
if let Some(extension) = &self.extension {
extension.serialize_changes_core(writer);
}
}
}
}
pub(super) fn hit_test(&self, pt: Point) -> bool {
let custom = self.visual.as_ref().and_then(WeakRef::upgrade).and_then(|visual| visual.custom_hit_test(pt));
let draw_list = self.draw_list.borrow().clone();
if draw_list.is_none() && custom.is_none() {
return false;
}
if let Some(custom) = custom {
return custom;
}
draw_list.is_some_and(|draw_list| draw_list.hit_test(pt))
}
pub(super) fn hit_test_geometry(&self, geometry: &Ref<Geometry>) -> IntersectionResult {
let custom =
self.visual.as_ref().and_then(WeakRef::upgrade).and_then(|visual| visual.custom_hit_test_geometry(geometry));
let draw_list = self.draw_list.borrow().clone();
if draw_list.is_none() && custom.is_none() {
return IntersectionResult::Empty;
}
if let Some(custom) = custom {
return custom;
}
draw_list.map_or(IntersectionResult::Empty, |draw_list| draw_list.hit_test_geometry(geometry))
}
}
impl CompositionExperimentalAcrylicVisualHooks for CompositionVisual {
fn base_serialize_changes_core(&self, writer: &mut BatchStreamWriter<'_>) {
if let CompositionVisualKind::DrawList(data) = &self.kind {
data.serialize_draw_list(self, writer);
}
}
}
#[derive(Clone)]
pub struct CompositionDrawListVisual(Rc<CompositionVisual>);
impl Deref for CompositionDrawListVisual {
type Target = Rc<CompositionVisual>;
fn deref(&self) -> &Rc<CompositionVisual> {
&self.0
}
}
impl CompositionDrawListVisual {
pub fn new(compositor: &Rc<Compositor>, visual: &Visual) -> CompositionDrawListVisual {
Self::create(compositor, visual, false)
}
pub fn with_extension(
compositor: &Rc<Compositor>,
visual: &Visual,
server_content: impl FnOnce() -> Box<dyn IServerVisualContent> + Send + 'static,
extension: Rc<dyn ICompositionDrawListVisualExtension>,
) -> CompositionDrawListVisual {
Self::create_core(compositor, Some(visual), false, Some(extension), server_content)
}
pub(super) fn create(compositor: &Rc<Compositor>, visual: &Visual, acrylic: bool) -> CompositionDrawListVisual {
Self::create_core(compositor, Some(visual), acrylic, None, move || {
if acrylic {
Box::new(ServerCompositionExperimentalAcrylicVisual::new())
} else {
Box::new(ServerCompositionDrawListVisual::new())
}
})
}
#[cfg(any(test, feature = "testing"))]
pub(crate) fn without_visual(compositor: &Rc<Compositor>) -> CompositionDrawListVisual {
Self::create_core(compositor, None, false, None, || Box::new(ServerCompositionDrawListVisual::new()))
}
fn create_core(
compositor: &Rc<Compositor>,
visual: Option<&Visual>,
acrylic: bool,
extension: Option<Rc<dyn ICompositionDrawListVisualExtension>>,
server_content: impl FnOnce() -> Box<dyn IServerVisualContent> + Send + 'static,
) -> CompositionDrawListVisual {
let data = DrawListData {
props: CompositionContainerVisualProps::new(),
visual: visual.map(|visual| visual.to_ref().downgrade()),
draw_list_changed: Cell::new(false),
draw_list: RefCell::new(None),
acrylic: acrylic.then(CompositionExperimentalAcrylicVisualProps::new),
extension,
};
let result = CompositionVisual::create(compositor, CompositionVisualKind::DrawList(data), server_content);
let has_custom_hit_test = visual.is_some_and(|visual| visual.custom_hit_test(Point::default()).is_some());
result.custom_hit_test_count_in_sub_tree.set(i32::from(has_custom_hit_test));
CompositionDrawListVisual(result)
}
pub fn from_visual(visual: &Rc<CompositionVisual>) -> Option<CompositionDrawListVisual> {
matches!(visual.kind, CompositionVisualKind::DrawList(_)).then(|| CompositionDrawListVisual(visual.clone()))
}
fn data(&self) -> &DrawListData {
match &self.0.kind {
CompositionVisualKind::DrawList(data) => data,
_ => unreachable!("a draw list visual handle wraps a draw list visual"),
}
}
pub fn visual(&self) -> Option<Ref<Visual>> {
self.data().visual.as_ref().and_then(WeakRef::upgrade)
}
pub fn draw_list(&self) -> Option<Rc<CompositionRenderData>> {
self.data().draw_list.borrow().clone()
}
pub fn set_draw_list(&self, value: Option<Rc<CompositionRenderData>>) {
let data = self.data();
if value.is_none() && data.draw_list.borrow().is_none() {
return;
}
let old = data.draw_list.replace(value);
if let Some(old) = old {
old.dispose();
}
data.draw_list_changed.set(true);
self.0.register_for_serialization();
}
}