use cotis::pipes::LayoutRenderPipe;
use cotis::utils::OwnedOrRef;
use cotis_defaults::element_configs::ElementConfig;
use cotis_defaults::render_commands::render_t_list::{IsRenderList, RenderTList};
use cotis_defaults::render_commands::{
Border, BorderWidth, ClipEnd, ClipStart, CornerRadii, Image, Rectangle, RenderCommandInfo, Text,
};
use cotis_layout::layout_struct::RenderCommandOutput;
use std::sync::Arc;
pub struct CotisLayoutToRenderListPipeForGenerics;
pub trait RenderCommandOutputDecodable<Custom>: Sized {
fn decode_list(output: &mut RenderCommandOutput<Custom>, buffer: &mut Vec<Self>);
}
pub trait RenderCommandOutputTListDecodable<Custom>: Sized {
fn decode_element(output: &mut RenderCommandOutput<Custom>, buffer: &mut Vec<Self>);
}
pub trait FromRenderCommandOutput<Custom>: Sized {
fn decode_element(output: &mut RenderCommandOutput<Custom>) -> Option<Self>;
}
impl<Custom, T: FromRenderCommandOutput<Custom> + Sized> RenderCommandOutputTListDecodable<Custom>
for T
{
fn decode_element(output: &mut RenderCommandOutput<Custom>, buffer: &mut Vec<Self>) {
match <T as FromRenderCommandOutput<Custom>>::decode_element(output) {
None => {}
Some(s) => {
buffer.push(s);
}
}
}
}
impl<Custom> RenderCommandOutputDecodable<Custom> for () {
fn decode_list(_output: &mut RenderCommandOutput<Custom>, _buffer: &mut Vec<Self>) {}
}
impl<Custom, Head, Tail> RenderCommandOutputDecodable<Custom> for RenderTList<Head, Tail>
where
Tail: IsRenderList + RenderCommandOutputDecodable<Custom>,
Head: RenderCommandOutputTListDecodable<Custom>,
{
fn decode_list(output: &mut RenderCommandOutput<Custom>, buffer: &mut Vec<Self>) {
let mut head_results = Vec::<Head>::new();
Head::decode_element(output, &mut head_results);
buffer.extend(head_results.into_iter().map(RenderTList::Type));
let mut tail_results = Vec::<Tail>::new();
Tail::decode_list(output, &mut tail_results);
buffer.extend(tail_results.into_iter().map(RenderTList::List));
}
}
impl<'render, S, I, C, E, Decodable>
LayoutRenderPipe<RenderCommandOutput<ElementConfig<'render, S, I, C, E>>, Decodable>
for CotisLayoutToRenderListPipeForGenerics
where
Decodable: RenderCommandOutputDecodable<ElementConfig<'render, S, I, C, E>>,
{
fn transform(
&mut self,
render_commands: impl Iterator<Item = RenderCommandOutput<ElementConfig<'render, S, I, C, E>>>,
) -> impl Iterator<Item = Decodable> {
let mut out = Vec::new();
for mut command in render_commands {
Decodable::decode_list(&mut command, &mut out);
}
out.into_iter()
}
}
impl<'render, S, I, C, E: 'render>
RenderCommandOutputTListDecodable<ElementConfig<'render, S, I, C, E>>
for ClipStart<'render, E>
{
fn decode_element(
output: &mut RenderCommandOutput<ElementConfig<'render, S, I, C, E>>,
buffer: &mut Vec<Self>,
) {
match output {
RenderCommandOutput::Element(_) => {}
RenderCommandOutput::ClipStart(start) => {
buffer.push(ClipStart {
info: RenderCommandInfo {
bounding_box: start.bounds,
id: start.id.get_id() as u32,
z_index: start.z_index as i16,
extra_data: None,
},
horizontal: false,
vertical: false,
corner_radii: CornerRadii {
top_left: 0.0,
top_right: 0.0,
bottom_left: 0.0,
bottom_right: 0.0,
},
})
}
RenderCommandOutput::ClipEnd(_) => {}
}
}
}
impl<'render, S, I, C, E: 'render>
RenderCommandOutputTListDecodable<ElementConfig<'render, S, I, C, E>> for ClipEnd
{
fn decode_element(
output: &mut RenderCommandOutput<ElementConfig<'render, S, I, C, E>>,
buffer: &mut Vec<Self>,
) {
match output {
RenderCommandOutput::Element(_) => {}
RenderCommandOutput::ClipStart(_) => {}
RenderCommandOutput::ClipEnd(_) => {
buffer.push(ClipEnd);
}
}
}
}
impl<'render, S, I, C, E: 'render + Send + Sync>
RenderCommandOutputTListDecodable<ElementConfig<'render, S, I, C, E>>
for Rectangle<'render, E>
{
fn decode_element(
output: &mut RenderCommandOutput<ElementConfig<'render, S, I, C, E>>,
buffer: &mut Vec<Self>,
) {
if let RenderCommandOutput::Element(element) = output {
#[cfg(not(feature = "complex_color"))]
if element.custom.style.background_color.a <= 0.0 {
return;
}
buffer.push(Rectangle {
info: RenderCommandInfo {
bounding_box: element.bounds,
id: element.custom.id_config.get_handle().get_id() as u32,
z_index: element.z_index as i16,
extra_data: element.custom.extra_data.as_mut().map(|s| s.share_clone()),
},
#[cfg(not(feature = "complex_color"))]
color: element.custom.style.background_color,
#[cfg(feature = "complex_color")]
color: element.custom.style.background_color.clone(),
corner_radii: CornerRadii {
top_left: element.custom.style.corner_radius.top_left,
top_right: element.custom.style.corner_radius.top_right,
bottom_left: element.custom.style.corner_radius.bottom_left,
bottom_right: element.custom.style.corner_radius.bottom_right,
},
})
}
}
}
impl<'render, S, I, C, E: 'render + Send + Sync>
RenderCommandOutputTListDecodable<ElementConfig<'render, S, I, C, E>> for Border<'render, E>
{
fn decode_element(
output: &mut RenderCommandOutput<ElementConfig<'render, S, I, C, E>>,
buffer: &mut Vec<Self>,
) {
if let RenderCommandOutput::Element(element) = output {
let w = &element.custom.style.border.width;
if w.left <= 0.0
&& w.right <= 0.0
&& w.top <= 0.0
&& w.bottom <= 0.0
&& w.between_children <= 0.0
{
return;
}
buffer.push(Border {
info: RenderCommandInfo {
bounding_box: element.bounds,
id: element.custom.id_config.get_handle().get_id() as u32,
z_index: element.z_index as i16,
extra_data: element.custom.extra_data.as_mut().map(|s| s.share_clone()),
},
color: element.custom.style.border.color,
width: BorderWidth {
left: w.left,
right: w.right,
top: w.top,
bottom: w.bottom,
between_children: w.between_children,
},
corner_radii: CornerRadii {
top_left: element.custom.style.corner_radius.top_left,
top_right: element.custom.style.corner_radius.top_right,
bottom_left: element.custom.style.corner_radius.bottom_left,
bottom_right: element.custom.style.corner_radius.bottom_right,
},
})
}
}
}
impl<'render, S, I: 'render, C, E: 'render + Send + Sync>
RenderCommandOutputTListDecodable<ElementConfig<'render, S, I, C, E>> for Image<'render, I, E>
{
fn decode_element(
output: &mut RenderCommandOutput<ElementConfig<'render, S, I, C, E>>,
buffer: &mut Vec<Self>,
) {
if let RenderCommandOutput::Element(element) = output {
buffer.push(Image {
data: match element.custom.image.take() {
None => return,
Some(s) => s.image,
},
info: RenderCommandInfo {
bounding_box: element.bounds,
id: element.custom.id_config.get_handle().get_id() as u32,
z_index: element.z_index as i16,
extra_data: element.custom.extra_data.as_mut().map(|s| s.share_clone()),
},
background_color: {
#[cfg(not(feature = "complex_color"))]
{
element.custom.style.background_color
}
#[cfg(feature = "complex_color")]
{
element.custom.style.background_color.clone()
}
},
corner_radii: CornerRadii {
top_left: element.custom.style.corner_radius.top_left,
top_right: element.custom.style.corner_radius.top_right,
bottom_left: element.custom.style.corner_radius.bottom_left,
bottom_right: element.custom.style.corner_radius.bottom_right,
},
})
}
}
}
impl<'render, S, I: 'render, C, E: 'render + Send + Sync>
RenderCommandOutputTListDecodable<ElementConfig<'render, S, I, C, E>> for Text<'render, E>
{
fn decode_element(
output: &mut RenderCommandOutput<ElementConfig<'render, S, I, C, E>>,
buffer: &mut Vec<Self>,
) {
if let RenderCommandOutput::Element(element) = output {
let text = match element.text.take() {
None => return,
Some(s) => s,
};
let text_s = text.get_str().to_owned();
buffer.push(Text {
info: RenderCommandInfo {
bounding_box: element.bounds,
id: element.custom.id_config.get_handle().get_id() as u32,
z_index: element.z_index as i16,
extra_data: element.custom.extra_data.as_mut().map(|s| s.share_clone()),
},
#[cfg(not(feature = "complex_colored_text"))]
color: text.style.color,
#[cfg(feature = "complex_colored_text")]
color: text.style.color.clone(),
font_id: text.config.font_id,
font_size: text.config.font_size,
letter_spacing: text.config.letter_spacing,
line_height: text.config.line_height,
text: OwnedOrRef::AsOwnedRef(Arc::new(text_s)),
})
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use cotis::utils::ElementIdConfig;
use cotis_defaults::colors::Color;
use cotis_defaults::element_configs::premade_configs::GenericElementConfig;
use cotis_defaults::element_configs::style::sizing::Sizing;
use cotis_defaults::element_configs::style::{
BorderElementStyle, BorderWidthStyle, CotisStyle,
};
use cotis_layout::layout_struct::RenderCommandOutputElement;
use cotis_utils::math::BoundingBox;
type TestConfig = GenericElementConfig<'static, (), ()>;
fn element_with_border(width: BorderWidthStyle) -> RenderCommandOutput<TestConfig> {
RenderCommandOutput::Element(RenderCommandOutputElement {
bounds: BoundingBox {
x: 0.0,
y: 0.0,
width: 100.0,
height: 50.0,
},
z_index: 0,
text: None,
custom: TestConfig {
id_config: ElementIdConfig::new_empty(),
style: CotisStyle {
border: BorderElementStyle {
color: Color::rgba(255.0, 0.0, 0.0, 255.0),
width,
},
..CotisStyle::<Sizing>::default()
},
image: None,
custom: None,
extra_data: None,
},
})
}
#[test]
fn border_emits_when_perimeter_positive_and_between_children_zero() {
let mut output = element_with_border(BorderWidthStyle {
left: 2.0,
right: 2.0,
top: 2.0,
bottom: 2.0,
between_children: 0.0,
});
let mut buffer = Vec::<Border<'static, ()>>::new();
Border::decode_element(&mut output, &mut buffer);
assert_eq!(buffer.len(), 1);
assert_eq!(buffer[0].width.left, 2.0);
assert_eq!(buffer[0].width.between_children, 0.0);
}
#[test]
fn border_skipped_when_all_widths_zero() {
let mut output = element_with_border(BorderWidthStyle {
left: 0.0,
right: 0.0,
top: 0.0,
bottom: 0.0,
between_children: 0.0,
});
let mut buffer = Vec::<Border<'static, ()>>::new();
Border::decode_element(&mut output, &mut buffer);
assert!(buffer.is_empty());
}
}