diff --git a/ui/src/nodes/image.rs b/ui/src/nodes/image.rs index 8b39463..6cbb734 100644 --- a/ui/src/nodes/image.rs +++ b/ui/src/nodes/image.rs @@ -1,16 +1,27 @@ -use std::fmt::Debug; -use femtovg::{ImageId, Paint, Path}; +use std::{fmt::Debug, mem, path::PathBuf}; +use femtovg::{Color, ErrorKind, ImageFlags, ImageId, Paint, Path}; use crate::{events::handler::EventHandlerDatabase, SharedNode, WeakNode}; use super::{Node, NodeChildren, Style}; #[derive(Debug)] -/// Simple image node. -/// This is basically just a wrapper around Rectangle node with a fill of type Paint::image. -/// Use that if you need more options. +/// Status of the image - when rendering, image node attempts to load the image and sets this status accordingly. +/// Changes this if you want to change the image. If the previous status was loaded, free the image. +/// In case the loading fails, image load status changes to Error and the node doesn't render. +pub enum ImageLoad { + LoadFile(PathBuf, ImageFlags), + // LoadArray(&[u8]), + LoadVec(Vec, ImageFlags), + Loaded(ImageId), + Error(ErrorKind) +} + +#[derive(Debug)] +/// Image node. +/// Sadly doesn't implement `Default` because of the `ImageLoad` enum. pub struct Image { pub style: Style, - /// The image to be rendered. You are responsible for freeing the image data. - pub image: ImageId, + /// The image to be rendered. + pub image: ImageLoad, /// Image width - note that you also have to set the style accordingly for it to render correctly, this is more about scaling the image pub width: f32, /// Image height - note that you also have to set the style accordingly for it to render correctly, this is more about scaling the image @@ -30,7 +41,33 @@ impl Node for Image { None } - fn render_pre_children(&self, context: &mut super::RenderContext, layout: taffy::prelude::Layout) { + fn render_pre_children(&mut self, context: &mut super::RenderContext, layout: taffy::prelude::Layout) { + match &self.image { + ImageLoad::LoadFile(_, _) => { + let image = mem::replace(&mut self.image, ImageLoad::Error(ErrorKind::UnknownError)); + if let ImageLoad::LoadFile(path, flags) = image { + match context.canvas.load_image_file(path, flags) { + Ok(image) => { + self.image = ImageLoad::Loaded(image); + }, + Err(e) => { + self.image = ImageLoad::Error(e); + } + } + } + }, + ImageLoad::LoadVec(data, flags) => { + match context.canvas.load_image_mem(data, *flags) { + Ok(image) => { + self.image = ImageLoad::Loaded(image); + }, + Err(e) => { + self.image = ImageLoad::Error(e); + } + } + }, + _ => {} + } let mut path = Path::new(); path.rounded_rect( 0., @@ -39,10 +76,18 @@ impl Node for Image { layout.size.height, self.radius ); - context.canvas.fill_path( - &path, - &Paint::image(self.image, 0., 0., self.width, self.height, 0., 1.) - ); + match &self.image { + ImageLoad::Loaded(image) => { + context.canvas.fill_path( + &path, + &Paint::image(*image, 0., 0., self.width, self.height, 0., 1.) + ); + }, + ImageLoad::Error(_) => { + context.canvas.fill_path(&path, &Paint::color(Color::rgb(255, 0, 0))) + }, + _ => unreachable!("We just loaded the image before, so it's either loaded or errored out.") + } } fn event_handlers(&self) -> Option { diff --git a/ui/src/nodes/mod.rs b/ui/src/nodes/mod.rs index 2b86ca3..5c30187 100644 --- a/ui/src/nodes/mod.rs +++ b/ui/src/nodes/mod.rs @@ -110,10 +110,10 @@ pub trait Node: Debug { /// Render the node, called before rendering it's children /// Canvas considers 0, 0 to be top left corner (for location after layouting happens) - fn render_pre_children(&self, _context: &mut RenderContext, _layout: Layout) {} + fn render_pre_children(&mut self, _context: &mut RenderContext, _layout: Layout) {} /// Render the node, called after rendering it's children /// Canvas considers 0, 0 to be top left corner (for location after layouting happens) - fn render_post_children(&self, _context: &mut RenderContext, _layout: Layout) {} + fn render_post_children(&mut self, _context: &mut RenderContext, _layout: Layout) {} /// Sets the parent node. /// May be called multiple times with the same value. @@ -340,12 +340,12 @@ pub(crate) fn render_recursively(node: &SharedNode, context: &mut RenderContext) } } drop(read_node); - sself.read().unwrap().render_pre_children(context, layout); + sself.write().unwrap().render_pre_children(context, layout); if let Some(children) = sself.read().unwrap().children() { for child in children { render_recursively(child, context); } } - sself.read().unwrap().render_post_children(context, layout); + sself.write().unwrap().render_post_children(context, layout); context.canvas.restore(); } \ No newline at end of file diff --git a/ui/src/nodes/primitives.rs b/ui/src/nodes/primitives.rs index 386338d..3886477 100644 --- a/ui/src/nodes/primitives.rs +++ b/ui/src/nodes/primitives.rs @@ -30,7 +30,7 @@ impl Node for Rectangle { fn children(&self) -> Option<&NodeChildren> { None } - fn render_pre_children(&self, context: &mut RenderContext, layout: Layout) { + fn render_pre_children(&mut self, context: &mut RenderContext, layout: Layout) { let mut path = Path::new(); path.rounded_rect( 0.,