ferroui_base/rendering/composition/brushes/
server_xaml_composition_brush.rs1use super::paint_composition_brush;
7use crate::media::immutable::ImmutableSolidColorBrush;
8use crate::media::{BoxShadows, Color, XamlBrushChanges};
9use crate::platform::IDrawingContextImpl;
10use crate::rendering::composition::generated::{ServerCompositionSimpleBrushHooks, ServerCompositionSimpleBrushProps};
11use crate::rendering::composition::server::{
12 impl_simple_server_render_resource, with_brush_light_ids, IServerObject, IServerRenderResource,
13 IServerRenderResourceObserver, ServerCompositor, SimpleServerRenderResource,
14};
15use crate::rendering::composition::transport::BatchStreamReader;
16use crate::{Rect, RoundedRect};
17use std::any::{Any, TypeId};
18use std::cell::{Cell, RefCell};
19use std::rc::{Rc, Weak};
20use std::sync::Arc;
21use std::time::Duration;
22
23pub struct ServerXamlCompositionBrush {
25 base: SimpleServerRenderResource,
26 this: Weak<ServerXamlCompositionBrush>,
27 props: ServerCompositionSimpleBrushProps,
28 fallback_color: Cell<Color>,
29 composition_brush: RefCell<Option<Rc<dyn IServerObject>>>,
30 light_target_ids: RefCell<Arc<[String]>>,
31 needs_transparent_window: Cell<bool>,
32 painting: Cell<bool>,
35}
36
37impl ServerXamlCompositionBrush {
38 pub fn new(compositor: &Rc<ServerCompositor>) -> Rc<ServerXamlCompositionBrush> {
39 Rc::new_cyclic(|this: &Weak<ServerXamlCompositionBrush>| ServerXamlCompositionBrush {
40 base: SimpleServerRenderResource::new(compositor, this.clone() as Weak<dyn IServerRenderResource>),
41 this: this.clone(),
42 props: ServerCompositionSimpleBrushProps::new(),
43 fallback_color: Cell::new(Color::default()),
44 composition_brush: RefCell::new(None),
45 light_target_ids: RefCell::new(Arc::from(Vec::<String>::new())),
46 needs_transparent_window: Cell::new(false),
47 painting: Cell::new(false),
48 })
49 }
50
51 pub fn is_disposed(&self) -> bool {
52 self.base.is_disposed()
53 }
54
55 pub fn brush_props(&self) -> &ServerCompositionSimpleBrushProps {
57 &self.props
58 }
59
60 pub fn fallback_color(&self) -> Color {
62 self.fallback_color.get()
63 }
64
65 pub fn composition_brush(&self) -> Option<Rc<dyn IServerObject>> {
67 self.composition_brush.borrow().clone()
68 }
69
70 pub fn light_target_ids(&self) -> Arc<[String]> {
72 self.light_target_ids.borrow().clone()
73 }
74
75 fn observer(&self) -> Option<Rc<dyn IServerRenderResourceObserver>> {
76 self.this.upgrade().map(|this| this as Rc<dyn IServerRenderResourceObserver>)
77 }
78
79 pub fn paint(&self, canvas: &mut dyn IDrawingContextImpl, rect: Rect) {
82 let opacity = self.props.opacity();
83 if opacity <= 0.0 || rect.width <= 0.0 || rect.height <= 0.0 || self.painting.replace(true) {
84 return;
85 }
86 if opacity < 1.0 {
87 canvas.push_opacity(opacity, Some(rect));
88 }
89 let window_is_opaque = self.needs_transparent_window.get()
92 && crate::rendering::composition::server::painting_visual()
93 .and_then(|visual| visual.root())
94 .is_none_or(|target| {
95 target.transparency_level() == crate::rendering::composition::CompositionTransparencyLevel::None
96 });
97 match self.composition_brush().filter(|_| !window_is_opaque) {
98 Some(brush) => {
99 let ids = self.light_target_ids();
100 with_brush_light_ids(ids, || paint_composition_brush(canvas, &brush, rect));
101 }
102 None => {
103 let color = self.fallback_color();
104 if color.a > 0 {
105 let brush = ImmutableSolidColorBrush::new(color);
106 canvas.draw_rectangle(Some(&brush), None, RoundedRect::from_rect(rect), &BoxShadows::default());
107 }
108 }
109 }
110 if opacity < 1.0 {
111 canvas.pop_opacity();
112 }
113 self.painting.set(false);
114 }
115
116 fn set_composition_brush(&self, brush: Option<Rc<dyn IServerObject>>) {
117 let old = std::mem::replace(&mut *self.composition_brush.borrow_mut(), brush);
118 let Some(observer) = self.observer() else { return };
119 if let Some(old) = old.and_then(|old| old.as_render_resource()) {
120 old.remove_observer(&observer);
121 }
122 if let Some(new) = self.composition_brush().and_then(|new| new.as_render_resource()) {
123 new.add_observer(&observer);
124 }
125 }
126}
127
128impl_simple_server_render_resource!(ServerXamlCompositionBrush, base);
129
130impl ServerCompositionSimpleBrushHooks for ServerXamlCompositionBrush {}
131
132impl IServerObject for ServerXamlCompositionBrush {
133 fn deserialize_changes_core(&self, reader: &mut BatchStreamReader<'_>, committed_at: Duration) {
134 self.props.deserialize_changes_core(self, reader, committed_at);
135 let Some(changes) = reader.read_value::<XamlBrushChanges>() else { return };
136 self.fallback_color.set(changes.fallback_color);
137 self.needs_transparent_window.set(changes.needs_transparent_window);
138 *self.light_target_ids.borrow_mut() = Arc::from(changes.light_target_ids);
139 let compositor = self.base.compositor();
140 let brush = changes.composition_brush.and_then(|id| compositor.as_ref().and_then(|compositor| compositor.get_object(id)));
141 self.set_composition_brush(brush);
142 }
143
144 fn values_invalidated(&self) {
145 self.base.core().invalidated(self);
146 }
147
148 fn dispose(&self) {
149 self.set_composition_brush(None);
150 self.base.core().dispose();
151 }
152
153 fn as_render_resource(self: Rc<Self>) -> Option<Rc<dyn IServerRenderResource>> {
154 Some(self)
155 }
156
157 fn as_brush(self: Rc<Self>) -> Option<Rc<dyn crate::media::IBrush>> {
161 let object: Rc<dyn IServerObject> = self;
162 crate::rendering::composition::drawing::painted_brush_of(&object)
163 }
164
165 fn get_props(&self, type_id: TypeId) -> Option<&dyn Any> {
166 self.props.find_props(type_id)
167 }
168
169 fn as_any(&self) -> &dyn Any {
170 self
171 }
172
173 fn into_any_rc(self: Rc<Self>) -> Rc<dyn Any> {
174 self
175 }
176}