ferroui_base/media/
xaml_composition_brush_base.rs1use crate::media::ref_adapter::RefAdapter;
6use crate::media::{Brush, BrushImpl, BrushImplExt, Color, IBrush, ServerBrushFactory};
7use crate::rendering::composition::brushes::ServerXamlCompositionBrush;
8use crate::rendering::composition::transport::BatchStreamWriter;
9use crate::rendering::composition::{CompositionBrush, Compositor};
10use crate::{ferro_class, ferro_property, FerroObjectImpl, FerroProperty, Ref, StyledProperty};
11use std::cell::RefCell;
12use std::rc::Rc;
13
14pub(crate) struct XamlBrushChanges {
18 pub(crate) fallback_color: Color,
19 pub(crate) composition_brush: Option<crate::rendering::composition::server::ServerObjectId>,
20 pub(crate) light_target_ids: Vec<String>,
21 pub(crate) needs_transparent_window: bool,
22}
23
24#[derive(Default)]
25struct XamlBrushState {
26 composition_brush: Option<Rc<CompositionBrush>>,
28 compositor: Option<Rc<Compositor>>,
30 light_target_ids: Vec<String>,
33 needs_transparent_window: bool,
37}
38
39#[repr(C)]
51pub struct XamlCompositionBrushBase {
52 base: Brush,
53 state: RefCell<XamlBrushState>,
54}
55
56ferro_class! {
57 XamlCompositionBrushBase: Brush, virtuals XamlCompositionBrushBaseImpl: BrushImpl {
58 fn on_connected(this);
61
62 fn on_disconnected(this);
65 }
66}
67
68crate::ferro_impl_classes!(XamlCompositionBrushBase: FerroObjectImpl);
69
70impl BrushImpl for XamlCompositionBrushBase {
71 fn factory(_this: &Self) -> Option<ServerBrushFactory> {
72 Some(|c| ServerXamlCompositionBrush::new(c))
73 }
74
75 fn on_referenced_from_compositor(this: &Self, c: &Rc<Compositor>) {
76 Self::parent_on_referenced_from_compositor(this, c);
77 let connect = {
78 let mut state = this.state.borrow_mut();
79 if state.compositor.is_none() {
80 state.compositor = Some(c.clone());
81 true
82 } else {
83 false
84 }
85 };
86 if connect {
87 this.on_connected();
88 }
89 }
90
91 fn on_unreferenced_from_compositor(this: &Self, c: &Rc<Compositor>) {
92 Self::parent_on_unreferenced_from_compositor(this, c);
93 let disconnect = this.state.borrow().compositor.as_ref().is_some_and(|compositor| Rc::ptr_eq(compositor, c));
94 if disconnect {
95 this.on_disconnected();
96 this.state.borrow_mut().compositor = None;
97 }
98 }
99
100 fn serialize_changes(this: &Self, c: &Compositor, writer: &mut BatchStreamWriter<'_>) {
101 Self::parent_serialize_changes(this, c, writer);
102 let state = this.state.borrow();
103 let composition_brush = state
104 .composition_brush
105 .as_ref()
106 .filter(|brush| std::ptr::eq(&**brush.compositor(), c) && !brush.is_disposed())
107 .map(|brush| brush.server());
108 writer.write_value(Some(XamlBrushChanges {
109 fallback_color: this.fallback_color(),
110 composition_brush,
111 light_target_ids: state.light_target_ids.clone(),
112 needs_transparent_window: state.needs_transparent_window,
113 }));
114 }
115}
116
117impl XamlCompositionBrushBaseImpl for XamlCompositionBrushBase {
118 fn on_connected(_this: &Self) {}
119
120 fn on_disconnected(_this: &Self) {}
121}
122
123crate::ferro_properties! { impl XamlCompositionBrushBase {
124 ferro_property!(
125 pub fn fallback_color_property() -> StyledProperty<Color> {
128 FerroProperty::register::<XamlCompositionBrushBase, _>("FallbackColor", Color::default())
129 }
130 );
131} }
132
133impl XamlCompositionBrushBase {
134 pub fn construct() -> Self {
136 Self { base: Brush::construct(), state: RefCell::new(XamlBrushState::default()) }
137 }
138
139 pub fn fallback_color(&self) -> Color {
141 self.get_value(Self::fallback_color_property())
142 }
143
144 pub fn set_fallback_color(&self, value: Color) {
145 self.set_value(Self::fallback_color_property(), value)
146 }
147
148 pub fn composition_brush(&self) -> Option<Rc<CompositionBrush>> {
151 self.state.borrow().composition_brush.clone()
152 }
153
154 pub fn set_composition_brush(&self, brush: Option<Rc<CompositionBrush>>) {
158 {
159 let mut state = self.state.borrow_mut();
160 let same = match (&state.composition_brush, &brush) {
161 (Some(a), Some(b)) => Rc::ptr_eq(a, b),
162 (None, None) => true,
163 _ => false,
164 };
165 if same {
166 return;
167 }
168 state.composition_brush = brush;
169 }
170 self.register_for_serialization();
174 }
175
176 pub fn needs_transparent_window(&self) -> bool {
180 self.state.borrow().needs_transparent_window
181 }
182
183 pub fn set_needs_transparent_window(&self, value: bool) {
184 if self.state.borrow().needs_transparent_window == value {
185 return;
186 }
187 self.state.borrow_mut().needs_transparent_window = value;
188 self.register_for_serialization();
189 }
190
191 pub fn compositor(&self) -> Option<Rc<Compositor>> {
193 self.state.borrow().compositor.clone()
194 }
195
196 pub fn is_connected(&self) -> bool {
199 self.state.borrow().compositor.is_some()
200 }
201
202 pub fn light_target_ids(&self) -> Vec<String> {
204 self.state.borrow().light_target_ids.clone()
205 }
206
207 pub fn add_light_target_id(&self, id: &str) {
210 {
211 let mut state = self.state.borrow_mut();
212 if state.light_target_ids.iter().any(|existing| existing == id) {
213 return;
214 }
215 state.light_target_ids.push(id.to_owned());
216 }
217 self.register_for_serialization();
218 }
219
220 pub fn remove_light_target_id(&self, id: &str) {
222 {
223 let mut state = self.state.borrow_mut();
224 let count = state.light_target_ids.len();
225 state.light_target_ids.retain(|existing| existing != id);
226 if state.light_target_ids.len() == count {
227 return;
228 }
229 }
230 self.register_for_serialization();
231 }
232
233 pub fn of(brush: &dyn IBrush) -> Option<Ref<XamlCompositionBrushBase>> {
235 brush.as_object().and_then(|object| object.downcast_ref::<XamlCompositionBrushBase>()).map(|brush| brush.to_ref())
236 }
237
238 pub fn as_brush(&self) -> Rc<dyn IBrush> {
240 Rc::new(RefAdapter(self.to_ref()))
241 }
242}
243
244crate::ferro_class_info!(XamlCompositionBrushBase {
245 markup: {
246 namespace: "FerroUI.Media",
247 },
248});