Skip to main content

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*/