use std::{collections::{hash_map::Entry, HashMap},
fmt::Debug,
ops::{AddAssign, Deref, DerefMut}};
use smallvec::smallvec;
use super::{paint::paint, render_op::RenderOp, ZOrder};
use crate::{ok, tui::DEBUG_TUI_SHOW_PIPELINE_EXPANDED, FlushKind, GlobalData, InlineVec,
LockedOutputDevice, RenderOps};
#[macro_export]
macro_rules! render_pipeline {
() => {
$crate::RenderPipeline::default()
};
(
@new
$arg_z_order: expr
=> $( /* Start a repetition. */
$arg_render_op: expr /* Expression. */
) /* End repetition. */
, /* Comma separated. */
* /* Zero or more times. */
$(,)* /* Optional trailing comma https://stackoverflow.com/a/43143459/2085356. */
) => {
{
let mut render_ops = $crate::RenderOps::default();
$(
render_ops.push($arg_render_op);
)*
let mut render_pipeline = $crate::RenderPipeline::default();
render_pipeline.push($arg_z_order, render_ops);
render_pipeline
}
};
(
@push_into
$arg_pipeline: ident
at $arg_z_order: expr
=> $($arg_render_op: expr),+
) => {
let mut render_ops = $crate::RenderOps::default();
$(
render_ops.push($arg_render_op);
)*
$arg_pipeline.push($arg_z_order, render_ops);
};
(
@join_and_drop
$($arg_other_pipeline: expr),+
) => {{
let mut pipeline = $crate::render_pipeline!();
$(
pipeline.join_into($arg_other_pipeline);
)*
pipeline
}};
(
@push_styled_texts_into
$arg_pipeline: ident
at $arg_z_order: expr
=> $arg_styled_texts: expr
) => {
let mut render_ops = $crate::RenderOps::default();
$crate::render_tui_styled_texts_into(&$arg_styled_texts, &mut render_ops);
$arg_pipeline.push($arg_z_order, render_ops);
};
}
type PipelineMap = HashMap<ZOrder, InlineVec<RenderOps>>;
#[derive(Default, Clone, PartialEq, Eq)]
pub struct RenderPipeline {
pub pipeline_map: PipelineMap,
}
impl RenderPipeline {
pub fn join_into(&mut self, mut rhs: RenderPipeline) {
for (z_order, mut rhs_render_ops_vec) in rhs.drain() {
match self.entry(z_order) {
Entry::Occupied(mut self_existing_entry) => {
let self_render_ops_vec = self_existing_entry.get_mut();
rhs_render_ops_vec.drain(..).for_each(|render_ops| {
self_render_ops_vec.push(render_ops);
});
}
Entry::Vacant(self_new_entry) => {
self_new_entry.insert(rhs_render_ops_vec);
}
}
}
}
pub fn push(&mut self, z_order: ZOrder, render_ops: RenderOps) {
match self.pipeline_map.entry(z_order) {
Entry::Occupied(mut existing_entry) => {
let render_ops_vec = existing_entry.get_mut();
render_ops_vec.push(render_ops);
}
Entry::Vacant(new_entry) => {
new_entry.insert(smallvec![render_ops]);
}
}
}
#[must_use]
pub fn get_all_render_op_in(&self, z_order: ZOrder) -> Option<InlineVec<RenderOp>> {
let vec_render_ops = self.pipeline_map.get(&z_order)?;
let mut vec_render_op: InlineVec<RenderOp> = smallvec![];
for render_ops in vec_render_ops {
for render_op in render_ops.iter() {
vec_render_op.push(render_op.clone());
}
}
Some(vec_render_op)
}
pub fn paint<S, AS>(
&self,
flush_kind: FlushKind,
global_data: &mut GlobalData<S, AS>,
locked_output_device: LockedOutputDevice<'_>,
is_mock: bool,
) where
S: Debug + Default + Clone + Sync + Send,
AS: Debug + Default + Clone + Sync + Send,
{
paint(self, flush_kind, global_data, locked_output_device, is_mock);
}
pub fn hoist(&mut self, z_order_from: ZOrder, z_order_to: ZOrder) {
if !self.pipeline_map.contains_key(&z_order_from) {
return;
}
let mut from = self.pipeline_map.remove(&z_order_from).unwrap_or_default();
match self.pipeline_map.entry(z_order_to) {
Entry::Occupied(mut to_existing_entry) => {
let to = to_existing_entry.get_mut();
from.drain(..).for_each(|render_ops| {
to.push(render_ops);
});
}
Entry::Vacant(to_new_entry) => {
to_new_entry.insert(from);
}
}
}
}
impl Deref for RenderPipeline {
type Target = PipelineMap;
fn deref(&self) -> &Self::Target { &self.pipeline_map }
}
impl DerefMut for RenderPipeline {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.pipeline_map }
}
impl Debug for RenderPipeline {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
const DELIM: &str = "\n - ";
let map = &**self;
for (count, (z_order, vec_render_ops)) in map.iter().enumerate() {
if count > 0 {
write!(f, "{DELIM}")?;
}
if DEBUG_TUI_SHOW_PIPELINE_EXPANDED {
write!(f, "[{z_order:?}] {vec_render_ops:?}")?;
} else {
write!(f, "[{z_order:?}] {:?} RenderOps", vec_render_ops.len())?;
}
}
ok!()
}
}
impl AddAssign for RenderPipeline {
fn add_assign(&mut self, other: RenderPipeline) { self.join_into(other); }
}