cotis-raylib 0.1.0-alpha

Raylib-backed renderer for Cotis
Documentation
//! `CotisRenderer` implementations for [`RaylibRender`].

use crate::drawables::{RaylibDrawContext, RaylibDrawable};
use crate::renderer::RaylibRender;
use cotis::renders::{CotisRenderer, CotisRendererAsync};
use cotis_defaults::render_commands::render_t_list::{IsRenderList, RenderTList};
use raylib::prelude::RaylibDraw;

impl<'b> CotisRenderer<Box<dyn RaylibDrawable + 'b>> for RaylibRender {
    fn draw_frame(&mut self, render_commands: impl Iterator<Item = Box<dyn RaylibDrawable + 'b>>) {
        self.load_new_font_sizes();
        let mut rl = self.lock_handle();
        let mut d = rl.begin_drawing(self.thread());
        d.clear_background(self.background_color);
        let mut clip_stack = Vec::new();
        let mut ctx = RaylibDrawContext {
            d: &mut d,
            fonts: self.fonts(),
            clip_stack: &mut clip_stack,
            image_cache: None,
        };
        for cmd in render_commands {
            cmd.draw(&mut ctx);
        }
    }
}

impl<'b> CotisRendererAsync<Box<dyn RaylibDrawable + 'b>> for RaylibRender {
    async fn draw_frame(
        &mut self,
        render_commands: impl Iterator<Item = Box<dyn RaylibDrawable + 'b>>,
    ) {
        self.load_new_font_sizes();
        let mut rl = self.lock_handle();
        let mut d = rl.begin_drawing(self.thread());
        d.clear_background(self.background_color);
        let mut clip_stack = Vec::new();
        let mut ctx = RaylibDrawContext {
            d: &mut d,
            fonts: self.fonts(),
            clip_stack: &mut clip_stack,
            image_cache: None,
        };
        for cmd in render_commands {
            cmd.draw(&mut ctx);
        }
    }
}

impl<T: RaylibDrawable, L: RaylibDrawable + IsRenderList> CotisRenderer<RenderTList<T, L>>
    for RaylibRender
{
    fn draw_frame(&mut self, render_commands: impl Iterator<Item = RenderTList<T, L>>) {
        self.load_new_font_sizes();
        let commands: Vec<_> = render_commands.collect();
        for cmd in &commands {
            cmd.preload_generic_images(self);
        }
        let mut rl = self.lock_handle();
        let mut d = rl.begin_drawing(self.thread());
        d.clear_background(self.background_color);
        let mut clip_stack = Vec::new();
        let mut ctx = RaylibDrawContext {
            d: &mut d,
            fonts: self.fonts(),
            clip_stack: &mut clip_stack,
            image_cache: Some(self.image_cache()),
        };
        for cmd in commands {
            Box::new(cmd).draw(&mut ctx);
        }
    }
}

impl<T: RaylibDrawable, L: RaylibDrawable + IsRenderList> CotisRendererAsync<RenderTList<T, L>>
    for RaylibRender
{
    async fn draw_frame(&mut self, render_commands: impl Iterator<Item = RenderTList<T, L>>) {
        CotisRenderer::draw_frame(self, render_commands);
    }
}

impl<T: RaylibDrawable> CotisRenderer<RenderTList<T, ()>> for RaylibRender {
    fn draw_frame(&mut self, render_commands: impl Iterator<Item = RenderTList<T, ()>>) {
        self.load_new_font_sizes();
        let commands: Vec<_> = render_commands.collect();
        for cmd in &commands {
            cmd.preload_generic_images(self);
        }
        let mut rl = self.lock_handle();
        let mut d = rl.begin_drawing(self.thread());
        d.clear_background(self.background_color);
        let mut clip_stack = Vec::new();
        let mut ctx = RaylibDrawContext {
            d: &mut d,
            fonts: self.fonts(),
            clip_stack: &mut clip_stack,
            image_cache: Some(self.image_cache()),
        };
        for cmd in commands {
            Box::new(cmd).draw(&mut ctx);
        }
    }
}

impl<T: RaylibDrawable> CotisRendererAsync<RenderTList<T, ()>> for RaylibRender {
    async fn draw_frame(&mut self, render_commands: impl Iterator<Item = RenderTList<T, ()>>) {
        CotisRenderer::draw_frame(self, render_commands);
    }
}

/*

/// Convert a collected stream of `RenderCommandOutput` (with `GenericCotisImage`) into
/// `Box<dyn RaylibDrawable>` commands, resolving images from the pre-populated cache.
/// This must be called AFTER all images have been pre-loaded into `image_cache`.
pub(crate) fn render_output_to_drawables<'b, C, E>(
    commands: Vec<RenderCommandOutput<ElementConfig<'b, GenericCotisImage, C, E>>>,
    image_cache: &HashMap<String, Arc<Texture2D>>,
) -> Vec<Box<dyn RaylibDrawable + 'b>>
where
    C: 'b,
    E: Send + Sync + 'b,
{
    let mut out: Vec<Box<dyn RaylibDrawable + 'b>> = Vec::new();
    let mut last_style: Option<::cotis::layout::declaration::style::CotisStyle> = None;

    for cmd in commands {
        match cmd {
            RenderCommandOutput::Element(el) => {
                let style = el.custom.style.clone();
                // Resolve image from cache (cloning the Arc) before consuming el.
                let image_path: Option<String> = el
                    .custom
                    .image
                    .as_ref()
                    .map(|ic| ic.image.as_ref().get_path().to_owned());
                let image_data: Option<OwnedOrRef<'b, Arc<Texture2D>>> =
                    image_path.as_deref().map(|path| {
                        OwnedOrRef::Owned(Box::new(image_cache.get(path).unwrap().clone()))
                    });
                last_style = Some(style);
                out.extend(element_to_drawables_with_image(el, image_data));
            }
            RenderCommandOutput::ClipStart(m) => {
                let style = last_style.as_ref().cloned().unwrap_or_default();
                out.push(Box::new(::cotis::renders::render_command::ClipStart {
                    info: ::cotis::renders::render_command::RenderCommandInfo {
                        bounding_box: m.bounds,
                        id: m.id,
                        z_index: m.z_index as i16,
                        extra_data: None::<OwnedOrRef<'b, E>>,
                    },
                    horizontal: style.clip.horizontal,
                    vertical: style.clip.vertical,
                    corner_radii: ::cotis::renders::render_command::CornerRadii {
                        top_left: style.corner_radius.top_left,
                        top_right: style.corner_radius.top_right,
                        bottom_left: style.corner_radius.bottom_left,
                        bottom_right: style.corner_radius.bottom_right,
                    },
                }));
            }
            RenderCommandOutput::ClipEnd(_m) => {
                out.push(Box::new(::cotis::renders::render_command::ClipEnd));
            }
        }
    }
    out
}

impl<'b, C, E> CotisRenderer<RenderCommandOutput<ElementConfig<'b, GenericCotisImage, C, E>>>
for RaylibRender
where
    C: 'b,
    E: Send + Sync + 'b,
{
    fn draw_frame(
        &mut self,
        render_commands: impl Iterator<Item = RenderCommandOutput<ElementConfig<'b, GenericCotisImage, C, E>>>,
    ) {
        self.load_new_font_sizes();
        // Collect first so we can pre-load textures before entering the draw context.
        let commands: Vec<_> = render_commands.collect();
        // Pre-load all textures referenced by image elements.
        for cmd in &commands {
            if let RenderCommandOutput::Element(el) = cmd {
                if let Some(ref ic) = el.custom.image {
                    self.translate_generic_image(ic.image.as_ref());
                }
            }
        }
        // Build all drawables (no borrow from self.rl needed here).
        let drawables = render_output_to_drawables(commands, &self.image_cache);
        // Now enter the drawing context.
        let mut rl = self.lock_handle();
        let mut d = rl.begin_drawing(self.thread());
        d.clear_background(self.background_color);
        let mut clip_stack = Vec::new();
        let mut ctx = RaylibDrawContext {
            d: &mut d,
            fonts: self.fonts(),
            clip_stack: &mut clip_stack,
            image_cache: None,
        };
        for cmd in drawables {
            cmd.draw(&mut ctx);
        }
    }
}

impl<'b, C, E> CotisRendererAsync<RenderCommandOutput<ElementConfig<'b, GenericCotisImage, C, E>>>
for RaylibRender
where
    C: 'b,
    E: Send + Sync + 'b,
{
    async fn draw_frame(
        &mut self,
        render_commands: impl Iterator<Item = RenderCommandOutput<ElementConfig<'b, GenericCotisImage, C, E>>>,
    ) {
        CotisRenderer::draw_frame(self, render_commands);
    }
}

*/