use smudge_core::element::{Element, Value};
use smudge_core::geometry::{Corners, Size};
use smudge_core::image::Image;
use smudge_core::paint::PaintOp;
use smudge_core::tree::Dirty;
use smudge_core::widget::{MeasureInput, PaintCx, Panel, Widget};
pub fn image(source: impl Into<Value<Option<Image>>>) -> Element<ImageView> {
Element::new(ImageView { source: None }).bind(SourceKey, source.into(), set_source)
}
struct SourceKey;
pub struct ImageView {
source: Option<Image>,
}
fn set_source(view: &mut ImageView, source: Option<Image>) -> Dirty {
let resized = match (&view.source, &source) {
(None, None) => false,
(Some(old), Some(new)) => old.width() != new.width() || old.height() != new.height(),
_ => true,
};
if view.source == source {
return Dirty::Clean;
}
view.source = source;
if resized { Dirty::Layout } else { Dirty::Paint }
}
impl Widget for ImageView {
fn measure(&mut self, input: MeasureInput) -> Option<Size> {
let image = self.source.as_ref()?;
let (width, height) = (image.width() as f32, image.height() as f32);
Some(match (input.known_width, input.known_height) {
(Some(known_width), Some(known_height)) => Size {
width: known_width,
height: known_height,
},
(Some(known_width), None) => Size {
width: known_width,
height: known_width * height / width,
},
(None, Some(known_height)) => Size {
width: known_height * width / height,
height: known_height,
},
(None, None) => Size {
width: width / input.scale,
height: height / input.scale,
},
})
}
fn paint(&self, cx: &PaintCx<'_>, out: &mut Vec<PaintOp>) {
Panel.paint(cx, out);
let Some(image) = &self.source else {
return;
};
let radius = cx.style.paint.corner_radius.resolve(cx.theme);
let left = cx.content.x0 - cx.rect.x0;
let top = cx.content.y0 - cx.rect.y0;
let right = cx.rect.x1 - cx.content.x1;
let bottom = cx.rect.y1 - cx.content.y1;
let inner = |a: f32, b: f32| (radius - a.max(b)).max(0.0);
out.push(PaintOp::Image {
rect: cx.content,
radius: Corners {
top_left: inner(left, top),
top_right: inner(right, top),
bottom_right: inner(right, bottom),
bottom_left: inner(left, bottom),
},
image: image.clone(),
});
}
}