use super::image::Handle;
use iced::{layout, Element, Hasher, Layout, Length, Point, Rectangle, Widget};
use std::hash::Hash;
use vek::{Aabr, Rgba, Vec2};
#[derive(Copy, Clone)]
pub enum GraphicKind {
Image(Handle, Rgba<u8>),
Color(Rgba<u8>),
Gradient(Rgba<u8>, Rgba<u8>),
}
pub struct Graphic {
aabr: Aabr<u16>,
kind: GraphicKind,
}
impl Graphic {
fn new(kind: GraphicKind, size: [u16; 2], offset: [u16; 2]) -> Self {
let size = Vec2::from(size);
let offset = Vec2::from(offset);
Self {
aabr: Aabr {
min: offset,
max: offset + size,
},
kind,
}
}
pub fn image(handle: Handle, size: [u16; 2], offset: [u16; 2]) -> Self {
Self::new(
GraphicKind::Image(handle, Rgba::broadcast(255)),
size,
offset,
)
}
#[must_use]
pub fn color(mut self, color: Rgba<u8>) -> Self {
match &mut self.kind {
GraphicKind::Image(_, c) => *c = color,
GraphicKind::Color(c) => *c = color,
GraphicKind::Gradient(_, _) => (),
}
self
}
pub fn gradient(
top_color: Rgba<u8>,
bottom_color: Rgba<u8>,
size: [u16; 2],
offset: [u16; 2],
) -> Self {
Self::new(GraphicKind::Gradient(top_color, bottom_color), size, offset)
}
pub fn rect(color: Rgba<u8>, size: [u16; 2], offset: [u16; 2]) -> Self {
Self::new(GraphicKind::Color(color), size, offset)
}
}
pub struct CompoundGraphic {
graphics: Vec<Graphic>,
graphics_size: [u16; 2],
width: Length,
height: Length,
fix_aspect_ratio: bool,
}
impl CompoundGraphic {
pub fn from_graphics(graphics: Vec<Graphic>) -> Self {
let width = Length::Fill;
let height = Length::Fill;
let graphics_size = graphics
.iter()
.fold(Vec2::zero(), |size, graphic| {
Vec2::max(size, graphic.aabr.max)
})
.into_array();
Self {
graphics,
graphics_size,
width,
height,
fix_aspect_ratio: false,
}
}
pub fn padded_image(image: Handle, size: [u16; 2], pad: [u16; 4]) -> Self {
let image = Graphic::image(image, size, [pad[0], pad[1]]);
let mut this = Self::from_graphics(vec![image]);
this.graphics_size[0] += pad[2];
this.graphics_size[1] += pad[3];
this
}
#[must_use]
pub fn width(mut self, width: Length) -> Self {
self.width = width;
self
}
#[must_use]
pub fn height(mut self, height: Length) -> Self {
self.height = height;
self
}
#[must_use]
pub fn fix_aspect_ratio(mut self) -> Self {
self.fix_aspect_ratio = true;
self
}
fn draw<R: Renderer>(&self, renderer: &mut R, layout: Layout<'_>) -> R::Output {
let [pixel_w, pixel_h] = self.graphics_size;
let bounds = layout.bounds();
let scale = Vec2::new(
bounds.width / pixel_w as f32,
bounds.height / pixel_h as f32,
);
let graphics = self.graphics.iter().map(|graphic| {
let bounds = {
let Aabr { min, max } = graphic.aabr.map(|e| e as f32);
let min = min * scale;
let size = max * scale - min;
Rectangle {
x: min.x + bounds.x,
y: min.y + bounds.y,
width: size.x,
height: size.y,
}
};
(bounds, graphic.kind)
});
renderer.draw(graphics)
}
}
impl<M, R> Widget<M, R> for CompoundGraphic
where
R: Renderer,
{
fn width(&self) -> Length { self.width }
fn height(&self) -> Length { self.height }
fn layout(&self, _renderer: &R, limits: &layout::Limits) -> layout::Node {
let mut size = limits.width(self.width).height(self.height).max();
if self.fix_aspect_ratio {
let aspect_ratio = {
let [w, h] = self.graphics_size;
w as f32 / h as f32
};
let max_aspect_ratio = size.width / size.height;
if max_aspect_ratio > aspect_ratio {
size.width = size.height * aspect_ratio;
} else {
size.height = size.width / aspect_ratio;
}
}
layout::Node::new(size)
}
fn draw(
&self,
renderer: &mut R,
_defaults: &R::Defaults,
layout: Layout<'_>,
_cursor_position: Point,
_viewport: &Rectangle,
) -> R::Output {
Self::draw(self, renderer, layout)
}
fn hash_layout(&self, state: &mut Hasher) {
struct Marker;
std::any::TypeId::of::<Marker>().hash(state);
self.width.hash(state);
self.height.hash(state);
if self.fix_aspect_ratio {
self.graphics_size.hash(state);
}
}
}
pub trait Renderer: iced::Renderer {
fn draw<I>(&mut self, graphics: I) -> Self::Output
where
I: Iterator<Item = (Rectangle, GraphicKind)>;
}
impl<'a, M, R> From<CompoundGraphic> for Element<'a, M, R>
where
R: Renderer,
{
fn from(compound_graphic: CompoundGraphic) -> Element<'a, M, R> {
Element::new(compound_graphic)
}
}
impl<R> super::background_container::Background<R> for CompoundGraphic
where
R: Renderer,
{
fn width(&self) -> Length { self.width }
fn height(&self) -> Length { self.height }
fn aspect_ratio_fixed(&self) -> bool { self.fix_aspect_ratio }
fn pixel_dims(&self, _renderer: &R) -> (u16, u16) {
(self.graphics_size[0], self.graphics_size[1])
}
fn draw(
&self,
renderer: &mut R,
_defaults: &R::Defaults,
layout: Layout<'_>,
_cursor_position: Point,
_viewport: &Rectangle,
) -> R::Output {
Self::draw(self, renderer, layout)
}
}