cotis_raylib/renderer/render_trait.rs
1//! `CotisRenderer` implementations for [`RaylibRender`].
2
3use crate::drawables::{RaylibDrawContext, RaylibDrawable};
4use crate::renderer::RaylibRender;
5use cotis::renders::{CotisRenderer, CotisRendererAsync};
6use cotis_defaults::render_commands::render_t_list::{IsRenderList, RenderTList};
7use raylib::prelude::RaylibDraw;
8
9impl<'b> CotisRenderer<Box<dyn RaylibDrawable + 'b>> for RaylibRender {
10 fn draw_frame(&mut self, render_commands: impl Iterator<Item = Box<dyn RaylibDrawable + 'b>>) {
11 self.load_new_font_sizes();
12 let mut rl = self.lock_handle();
13 let mut d = rl.begin_drawing(self.thread());
14 d.clear_background(self.background_color);
15 let mut clip_stack = Vec::new();
16 let mut ctx = RaylibDrawContext {
17 d: &mut d,
18 fonts: self.fonts(),
19 clip_stack: &mut clip_stack,
20 image_cache: None,
21 };
22 for cmd in render_commands {
23 cmd.draw(&mut ctx);
24 }
25 }
26}
27
28impl<'b> CotisRendererAsync<Box<dyn RaylibDrawable + 'b>> for RaylibRender {
29 async fn draw_frame(
30 &mut self,
31 render_commands: impl Iterator<Item = Box<dyn RaylibDrawable + 'b>>,
32 ) {
33 self.load_new_font_sizes();
34 let mut rl = self.lock_handle();
35 let mut d = rl.begin_drawing(self.thread());
36 d.clear_background(self.background_color);
37 let mut clip_stack = Vec::new();
38 let mut ctx = RaylibDrawContext {
39 d: &mut d,
40 fonts: self.fonts(),
41 clip_stack: &mut clip_stack,
42 image_cache: None,
43 };
44 for cmd in render_commands {
45 cmd.draw(&mut ctx);
46 }
47 }
48}
49
50impl<T: RaylibDrawable, L: RaylibDrawable + IsRenderList> CotisRenderer<RenderTList<T, L>>
51 for RaylibRender
52{
53 fn draw_frame(&mut self, render_commands: impl Iterator<Item = RenderTList<T, L>>) {
54 self.load_new_font_sizes();
55 let commands: Vec<_> = render_commands.collect();
56 for cmd in &commands {
57 cmd.preload_generic_images(self);
58 }
59 let mut rl = self.lock_handle();
60 let mut d = rl.begin_drawing(self.thread());
61 d.clear_background(self.background_color);
62 let mut clip_stack = Vec::new();
63 let mut ctx = RaylibDrawContext {
64 d: &mut d,
65 fonts: self.fonts(),
66 clip_stack: &mut clip_stack,
67 image_cache: Some(self.image_cache()),
68 };
69 for cmd in commands {
70 Box::new(cmd).draw(&mut ctx);
71 }
72 }
73}
74
75impl<T: RaylibDrawable, L: RaylibDrawable + IsRenderList> CotisRendererAsync<RenderTList<T, L>>
76 for RaylibRender
77{
78 async fn draw_frame(&mut self, render_commands: impl Iterator<Item = RenderTList<T, L>>) {
79 CotisRenderer::draw_frame(self, render_commands);
80 }
81}
82
83impl<T: RaylibDrawable> CotisRenderer<RenderTList<T, ()>> for RaylibRender {
84 fn draw_frame(&mut self, render_commands: impl Iterator<Item = RenderTList<T, ()>>) {
85 self.load_new_font_sizes();
86 let commands: Vec<_> = render_commands.collect();
87 for cmd in &commands {
88 cmd.preload_generic_images(self);
89 }
90 let mut rl = self.lock_handle();
91 let mut d = rl.begin_drawing(self.thread());
92 d.clear_background(self.background_color);
93 let mut clip_stack = Vec::new();
94 let mut ctx = RaylibDrawContext {
95 d: &mut d,
96 fonts: self.fonts(),
97 clip_stack: &mut clip_stack,
98 image_cache: Some(self.image_cache()),
99 };
100 for cmd in commands {
101 Box::new(cmd).draw(&mut ctx);
102 }
103 }
104}
105
106impl<T: RaylibDrawable> CotisRendererAsync<RenderTList<T, ()>> for RaylibRender {
107 async fn draw_frame(&mut self, render_commands: impl Iterator<Item = RenderTList<T, ()>>) {
108 CotisRenderer::draw_frame(self, render_commands);
109 }
110}
111
112/*
113
114/// Convert a collected stream of `RenderCommandOutput` (with `GenericCotisImage`) into
115/// `Box<dyn RaylibDrawable>` commands, resolving images from the pre-populated cache.
116/// This must be called AFTER all images have been pre-loaded into `image_cache`.
117pub(crate) fn render_output_to_drawables<'b, C, E>(
118 commands: Vec<RenderCommandOutput<ElementConfig<'b, GenericCotisImage, C, E>>>,
119 image_cache: &HashMap<String, Arc<Texture2D>>,
120) -> Vec<Box<dyn RaylibDrawable + 'b>>
121where
122 C: 'b,
123 E: Send + Sync + 'b,
124{
125 let mut out: Vec<Box<dyn RaylibDrawable + 'b>> = Vec::new();
126 let mut last_style: Option<::cotis::layout::declaration::style::CotisStyle> = None;
127
128 for cmd in commands {
129 match cmd {
130 RenderCommandOutput::Element(el) => {
131 let style = el.custom.style.clone();
132 // Resolve image from cache (cloning the Arc) before consuming el.
133 let image_path: Option<String> = el
134 .custom
135 .image
136 .as_ref()
137 .map(|ic| ic.image.as_ref().get_path().to_owned());
138 let image_data: Option<OwnedOrRef<'b, Arc<Texture2D>>> =
139 image_path.as_deref().map(|path| {
140 OwnedOrRef::Owned(Box::new(image_cache.get(path).unwrap().clone()))
141 });
142 last_style = Some(style);
143 out.extend(element_to_drawables_with_image(el, image_data));
144 }
145 RenderCommandOutput::ClipStart(m) => {
146 let style = last_style.as_ref().cloned().unwrap_or_default();
147 out.push(Box::new(::cotis::renders::render_command::ClipStart {
148 info: ::cotis::renders::render_command::RenderCommandInfo {
149 bounding_box: m.bounds,
150 id: m.id,
151 z_index: m.z_index as i16,
152 extra_data: None::<OwnedOrRef<'b, E>>,
153 },
154 horizontal: style.clip.horizontal,
155 vertical: style.clip.vertical,
156 corner_radii: ::cotis::renders::render_command::CornerRadii {
157 top_left: style.corner_radius.top_left,
158 top_right: style.corner_radius.top_right,
159 bottom_left: style.corner_radius.bottom_left,
160 bottom_right: style.corner_radius.bottom_right,
161 },
162 }));
163 }
164 RenderCommandOutput::ClipEnd(_m) => {
165 out.push(Box::new(::cotis::renders::render_command::ClipEnd));
166 }
167 }
168 }
169 out
170}
171
172impl<'b, C, E> CotisRenderer<RenderCommandOutput<ElementConfig<'b, GenericCotisImage, C, E>>>
173for RaylibRender
174where
175 C: 'b,
176 E: Send + Sync + 'b,
177{
178 fn draw_frame(
179 &mut self,
180 render_commands: impl Iterator<Item = RenderCommandOutput<ElementConfig<'b, GenericCotisImage, C, E>>>,
181 ) {
182 self.load_new_font_sizes();
183 // Collect first so we can pre-load textures before entering the draw context.
184 let commands: Vec<_> = render_commands.collect();
185 // Pre-load all textures referenced by image elements.
186 for cmd in &commands {
187 if let RenderCommandOutput::Element(el) = cmd {
188 if let Some(ref ic) = el.custom.image {
189 self.translate_generic_image(ic.image.as_ref());
190 }
191 }
192 }
193 // Build all drawables (no borrow from self.rl needed here).
194 let drawables = render_output_to_drawables(commands, &self.image_cache);
195 // Now enter the drawing context.
196 let mut rl = self.lock_handle();
197 let mut d = rl.begin_drawing(self.thread());
198 d.clear_background(self.background_color);
199 let mut clip_stack = Vec::new();
200 let mut ctx = RaylibDrawContext {
201 d: &mut d,
202 fonts: self.fonts(),
203 clip_stack: &mut clip_stack,
204 image_cache: None,
205 };
206 for cmd in drawables {
207 cmd.draw(&mut ctx);
208 }
209 }
210}
211
212impl<'b, C, E> CotisRendererAsync<RenderCommandOutput<ElementConfig<'b, GenericCotisImage, C, E>>>
213for RaylibRender
214where
215 C: 'b,
216 E: Send + Sync + 'b,
217{
218 async fn draw_frame(
219 &mut self,
220 render_commands: impl Iterator<Item = RenderCommandOutput<ElementConfig<'b, GenericCotisImage, C, E>>>,
221 ) {
222 CotisRenderer::draw_frame(self, render_commands);
223 }
224}
225
226*/