kitsune_ui 0.5.0

A retained-mode UI library in rust
Documentation
use wgpu::util::DeviceExt;

use crate::{context::Context, view::GlobalPosition};

pub trait Render {
	fn render<'a, 'b>(&'a self, context: &mut Context<RenderContext<'b>>)
	where
		'a: 'b;
}

#[repr(C)]
#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
pub struct Vertex {
	position: GlobalPosition,
	uv: [f32; 2],
}

impl Vertex {
	const LAYOUT: [wgpu::VertexAttribute; 2] =
		wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2];

	pub fn layout() -> wgpu::VertexBufferLayout<'static> {
		wgpu::VertexBufferLayout {
			array_stride: std::mem::size_of::<Self>() as u64,
			step_mode: wgpu::VertexStepMode::Vertex,
			attributes: &Self::LAYOUT,
		}
	}

	pub fn new(position: GlobalPosition, uv: [f32; 2]) -> Self {
		Self { position, uv }
	}
}

pub struct RenderContext<'a> {
	pass: wgpu::RenderPass<'a>,
}

impl<'a> RenderContext<'a> {
	pub fn new(pass: wgpu::RenderPass<'a>) -> Self {
		Self { pass }
	}
}

pub struct RenderedMesh {
	vertices: wgpu::Buffer,
	indices: wgpu::Buffer,
	num_indices: u32,
	bind_group: wgpu::BindGroup,
}

impl RenderedMesh {
	pub fn new(
		device: &wgpu::Device,
		vertices: &[Vertex],
		indices: &[u16],
		bind_group: wgpu::BindGroup,
	) -> Self {
		let vertices = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
			label: Some("Vertex Buffer"),
			contents: bytemuck::cast_slice(vertices),
			usage: wgpu::BufferUsages::VERTEX,
		});

		let num_indices = indices.len() as u32;

		let indices = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
			label: Some("Index Buffer"),
			contents: bytemuck::cast_slice(indices),
			usage: wgpu::BufferUsages::INDEX,
		});

		Self {
			vertices,
			indices,
			num_indices,
			bind_group,
		}
	}
}

mod impls {
	use paste::paste;

	use super::*;

	impl Render for RenderedMesh {
		fn render<'a, 'b>(&'a self, context: &mut Context<RenderContext<'b>>)
		where
			'a: 'b,
		{
			context
				.pass
				.set_bind_group(0, &self.bind_group, &[]);
			context
				.pass
				.set_vertex_buffer(0, self.vertices.slice(..));
			context
				.pass
				.set_index_buffer(self.indices.slice(..), wgpu::IndexFormat::Uint16);
			context
				.pass
				.draw_indexed(0..self.num_indices, 0, 0..1);
		}
	}

	impl<T> Render for Vec<T>
	where
		T: Render,
	{
		fn render<'a, 'b>(&'a self, context: &mut Context<RenderContext<'b>>)
		where
			'a: 'b,
		{
			for renderable in self {
				renderable.render(context);
			}
		}
	}

	impl<T> Render for Option<T>
	where
		T: Render,
	{
		fn render<'a, 'b>(&'a self, context: &mut Context<RenderContext<'b>>)
		where
			'a: 'b,
		{
			if let Some(renderable) = self {
				renderable.render(context);
			}
		}
	}

	impl<T> Render for std::rc::Rc<T>
	where
		T: Render,
	{
		fn render<'a, 'b>(&'a self, context: &mut Context<RenderContext<'b>>)
		where
			'a: 'b,
		{
			(**self).render(context);
		}
	}

	macro_rules! tuple_impl {
    ($($name:ident),*) => {
        impl<$($name: Render),*> Render for ($($name),*) {
        	fn render<'a, 'b>(&'a self, context: &mut crate::context::Context<crate::render::RenderContext<'b>>) where 'a: 'b{
    			paste! {
    				let ($([<$name:snake>]),*) = self;
    				$(
    				    <$name as crate::render::Render>::render([<$name:snake>], context);
    				)*
    			}
    		}
        }
    };
}

	macro_rules! tuples_impl {
	($(($($name:ident),*)),*) => {
	    $(
	        tuple_impl!($($name),*);
	    )*
	};
}

	tuples_impl!(
		(A, B),
		(A, B, C),
		(A, B, C, D),
		(A, B, C, D, E),
		(A, B, C, D, E, F),
		(A, B, C, D, E, F, G),
		(A, B, C, D, E, F, G, H)
	);
}