ferroui_base/rendering/composition/
composition_draw_list_visual.rs1use super::drawing::CompositionRenderData;
2use super::generated::{
3 CompositionContainerVisualProps, CompositionExperimentalAcrylicVisualHooks,
4 CompositionExperimentalAcrylicVisualProps,
5};
6use super::server::{IServerVisualContent, ServerCompositionDrawListVisual, ServerCompositionExperimentalAcrylicVisual};
7use super::transport::{BatchStreamWriter, IRegisterForSerialization};
8use super::visual::CompositionVisualKind;
9use super::{CompositionVisual, Compositor};
10use crate::media::{Geometry, IntersectionResult};
11use crate::{Point, Ref, Visual, WeakRef};
12use std::cell::{Cell, RefCell};
13use std::ops::Deref;
14use std::rc::Rc;
15
16pub trait ICompositionDrawListVisualExtension: 'static {
20 fn serialize_changes_core(&self, writer: &mut BatchStreamWriter<'_>);
25}
26
27pub(crate) struct DrawListData {
30 props: CompositionContainerVisualProps,
31 visual: Option<WeakRef<Visual>>,
35 draw_list_changed: Cell<bool>,
36 draw_list: RefCell<Option<Rc<CompositionRenderData>>>,
37 pub(super) acrylic: Option<CompositionExperimentalAcrylicVisualProps>,
39 extension: Option<Rc<dyn ICompositionDrawListVisualExtension>>,
41}
42
43impl DrawListData {
44 pub(super) fn props(&self) -> &CompositionContainerVisualProps {
45 &self.props
46 }
47
48 pub(super) fn initialize_defaults(&self, host: &CompositionVisual) {
49 self.props.initialize_defaults(host);
50 if let Some(acrylic) = &self.acrylic {
51 acrylic.initialize_own_defaults(host);
52 }
53 }
54
55 fn serialize_draw_list(&self, host: &CompositionVisual, writer: &mut BatchStreamWriter<'_>) {
56 writer.write(u8::from(self.draw_list_changed.get()));
57 if self.draw_list_changed.get() {
58 writer.write_server_object(self.draw_list.borrow().as_ref().map(|draw_list| draw_list.server()));
59 self.draw_list_changed.set(false);
60 }
61 self.props.serialize_changes_core(host, writer);
62 }
63
64 pub(super) fn serialize_changes_core(&self, host: &CompositionVisual, writer: &mut BatchStreamWriter<'_>) {
65 match &self.acrylic {
66 Some(acrylic) => acrylic.serialize_changes_core(host, writer),
69 None => {
70 self.serialize_draw_list(host, writer);
71 if let Some(extension) = &self.extension {
72 extension.serialize_changes_core(writer);
73 }
74 }
75 }
76 }
77
78 pub(super) fn hit_test(&self, pt: Point) -> bool {
79 let custom = self.visual.as_ref().and_then(WeakRef::upgrade).and_then(|visual| visual.custom_hit_test(pt));
80 let draw_list = self.draw_list.borrow().clone();
81 if draw_list.is_none() && custom.is_none() {
82 return false;
83 }
84 if let Some(custom) = custom {
85 return custom;
86 }
87 draw_list.is_some_and(|draw_list| draw_list.hit_test(pt))
88 }
89
90 pub(super) fn hit_test_geometry(&self, geometry: &Ref<Geometry>) -> IntersectionResult {
91 let custom =
92 self.visual.as_ref().and_then(WeakRef::upgrade).and_then(|visual| visual.custom_hit_test_geometry(geometry));
93 let draw_list = self.draw_list.borrow().clone();
94 if draw_list.is_none() && custom.is_none() {
95 return IntersectionResult::Empty;
96 }
97 if let Some(custom) = custom {
98 return custom;
99 }
100 draw_list.map_or(IntersectionResult::Empty, |draw_list| draw_list.hit_test_geometry(geometry))
101 }
102}
103
104impl CompositionExperimentalAcrylicVisualHooks for CompositionVisual {
105 fn base_serialize_changes_core(&self, writer: &mut BatchStreamWriter<'_>) {
106 if let CompositionVisualKind::DrawList(data) = &self.kind {
107 data.serialize_draw_list(self, writer);
108 }
109 }
110}
111
112#[derive(Clone)]
115pub struct CompositionDrawListVisual(Rc<CompositionVisual>);
116
117impl Deref for CompositionDrawListVisual {
118 type Target = Rc<CompositionVisual>;
119
120 fn deref(&self) -> &Rc<CompositionVisual> {
121 &self.0
122 }
123}
124
125impl CompositionDrawListVisual {
126 pub fn new(compositor: &Rc<Compositor>, visual: &Visual) -> CompositionDrawListVisual {
128 Self::create(compositor, visual, false)
129 }
130
131 pub fn with_extension(
136 compositor: &Rc<Compositor>,
137 visual: &Visual,
138 server_content: impl FnOnce() -> Box<dyn IServerVisualContent> + Send + 'static,
139 extension: Rc<dyn ICompositionDrawListVisualExtension>,
140 ) -> CompositionDrawListVisual {
141 Self::create_core(compositor, Some(visual), false, Some(extension), server_content)
142 }
143
144 pub(super) fn create(compositor: &Rc<Compositor>, visual: &Visual, acrylic: bool) -> CompositionDrawListVisual {
145 Self::create_core(compositor, Some(visual), acrylic, None, move || {
146 if acrylic {
147 Box::new(ServerCompositionExperimentalAcrylicVisual::new())
148 } else {
149 Box::new(ServerCompositionDrawListVisual::new())
150 }
151 })
152 }
153
154 #[cfg(any(test, feature = "testing"))]
160 pub(crate) fn without_visual(compositor: &Rc<Compositor>) -> CompositionDrawListVisual {
161 Self::create_core(compositor, None, false, None, || Box::new(ServerCompositionDrawListVisual::new()))
162 }
163
164 fn create_core(
165 compositor: &Rc<Compositor>,
166 visual: Option<&Visual>,
167 acrylic: bool,
168 extension: Option<Rc<dyn ICompositionDrawListVisualExtension>>,
169 server_content: impl FnOnce() -> Box<dyn IServerVisualContent> + Send + 'static,
170 ) -> CompositionDrawListVisual {
171 let data = DrawListData {
172 props: CompositionContainerVisualProps::new(),
173 visual: visual.map(|visual| visual.to_ref().downgrade()),
174 draw_list_changed: Cell::new(false),
175 draw_list: RefCell::new(None),
176 acrylic: acrylic.then(CompositionExperimentalAcrylicVisualProps::new),
177 extension,
178 };
179 let result = CompositionVisual::create(compositor, CompositionVisualKind::DrawList(data), server_content);
180 let has_custom_hit_test = visual.is_some_and(|visual| visual.custom_hit_test(Point::default()).is_some());
183 result.custom_hit_test_count_in_sub_tree.set(i32::from(has_custom_hit_test));
184 CompositionDrawListVisual(result)
185 }
186
187 pub fn from_visual(visual: &Rc<CompositionVisual>) -> Option<CompositionDrawListVisual> {
189 matches!(visual.kind, CompositionVisualKind::DrawList(_)).then(|| CompositionDrawListVisual(visual.clone()))
190 }
191
192 fn data(&self) -> &DrawListData {
193 match &self.0.kind {
194 CompositionVisualKind::DrawList(data) => data,
195 _ => unreachable!("a draw list visual handle wraps a draw list visual"),
196 }
197 }
198
199 pub fn visual(&self) -> Option<Ref<Visual>> {
202 self.data().visual.as_ref().and_then(WeakRef::upgrade)
203 }
204
205 pub fn draw_list(&self) -> Option<Rc<CompositionRenderData>> {
206 self.data().draw_list.borrow().clone()
207 }
208
209 pub fn set_draw_list(&self, value: Option<Rc<CompositionRenderData>>) {
210 let data = self.data();
211 if value.is_none() && data.draw_list.borrow().is_none() {
213 return;
214 }
215
216 let old = data.draw_list.replace(value);
217 if let Some(old) = old {
218 old.dispose();
219 }
220 data.draw_list_changed.set(true);
221 self.0.register_for_serialization();
222 }
223}