use std::{fmt::{Debug, Formatter, Result},
ops::{AddAssign, Deref, DerefMut}};
use super::TERMINAL_LIB_BACKEND;
use crate::{ok, CrosstermDebugFormatRenderOp, InlineString, InlineVec,
LockedOutputDevice, PaintRenderOp, Pos, RenderOpImplCrossterm, Size,
TerminalLibBackend, TuiColor, TuiStyle};
#[macro_export]
macro_rules! render_ops {
() => {
$crate::RenderOps::default()
};
(
@new
$( /* 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.list.push($arg_render_op);
)*
render_ops
}
};
(
@add_to
$arg_render_ops: 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. */
) => {
{
$(
$arg_render_ops.list.push($arg_render_op);
)*
}
};
(
@render_styled_texts_into
$arg_render_ops: expr
=>
$( /* Start a repetition. */
$arg_styled_texts: expr/* Expression. */
) /* End repetition. */
, /* Comma separated. */
* /* Zero or more times. */
$(,)* /* Optional trailing comma https://stackoverflow.com/a/43143459/2085356. */
) => {
{
$(
$crate::render_tui_styled_texts_into(&$arg_styled_texts, &mut $arg_render_ops);
)*
}
};
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct RenderOps {
pub list: InlineVec<RenderOp>,
}
impl Default for RenderOps {
fn default() -> Self {
Self {
list: InlineVec::new(),
}
}
}
#[derive(Default, Debug)]
pub struct RenderOpsLocalData {
pub cursor_pos: Pos,
}
pub mod render_ops_impl {
use super::{ok, AddAssign, Debug, Deref, DerefMut, InlineVec, LockedOutputDevice,
PaintRenderOp, RenderOp, RenderOpImplCrossterm, RenderOps,
RenderOpsLocalData, Size, TerminalLibBackend, TERMINAL_LIB_BACKEND};
impl RenderOps {
pub fn execute_all(
&self,
skip_flush: &mut bool,
window_size: Size,
locked_output_device: LockedOutputDevice<'_>,
is_mock: bool,
) {
let mut local_data = RenderOpsLocalData::default();
for render_op in &self.list {
RenderOps::route_paint_render_op_to_backend(
&mut local_data,
skip_flush,
render_op,
window_size,
locked_output_device,
is_mock,
);
}
}
pub fn route_paint_render_op_to_backend(
local_data: &mut RenderOpsLocalData,
skip_flush: &mut bool,
render_op: &RenderOp,
window_size: Size,
locked_output_device: LockedOutputDevice<'_>,
is_mock: bool,
) {
match TERMINAL_LIB_BACKEND {
TerminalLibBackend::Crossterm => {
RenderOpImplCrossterm {}.paint(
skip_flush,
render_op,
window_size,
local_data,
locked_output_device,
is_mock,
);
}
TerminalLibBackend::Termion => unimplemented!(),
}
}
}
impl Deref for RenderOps {
type Target = InlineVec<RenderOp>;
fn deref(&self) -> &Self::Target { &self.list }
}
impl DerefMut for RenderOps {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.list }
}
impl AddAssign<RenderOp> for RenderOps {
fn add_assign(&mut self, rhs: RenderOp) { self.list.push(rhs); }
}
impl Debug for RenderOps {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
const DELIM: &str = "\n - ";
let mut iter = self.iter();
write!(f, "RenderOps.len(): {}", self.list.len()).ok();
if let Some(first) = iter.next() {
write!(f, "[{first:?}]").ok();
}
for item in iter {
write!(f, "{DELIM}[{item:?}]").ok();
}
ok!()
}
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum RenderOp {
EnterRawMode,
ExitRawMode,
MoveCursorPositionAbs( Pos),
MoveCursorPositionRelTo(
Pos,
Pos,
),
ClearScreen,
SetFgColor(TuiColor),
SetBgColor(TuiColor),
ResetColor,
ApplyColors(Option<TuiStyle>),
PaintTextWithAttributes(InlineString, Option<TuiStyle>),
CompositorNoClipTruncPaintTextWithAttributes(InlineString, Option<TuiStyle>),
Noop,
}
mod render_op_impl {
use super::{CrosstermDebugFormatRenderOp, Debug, DebugFormatRenderOp, Formatter,
RenderOp, Result, TerminalLibBackend, TERMINAL_LIB_BACKEND};
impl Default for RenderOp {
fn default() -> Self { Self::Noop }
}
impl Debug for RenderOp {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match TERMINAL_LIB_BACKEND {
TerminalLibBackend::Crossterm => {
CrosstermDebugFormatRenderOp {}.fmt_debug(self, f)
}
TerminalLibBackend::Termion => unimplemented!(),
}
}
}
}
mod render_op_impl_trait_flush {
use super::{Flush, LockedOutputDevice, RenderOp, RenderOpImplCrossterm,
TerminalLibBackend, TERMINAL_LIB_BACKEND};
impl Flush for RenderOp {
fn flush(&mut self, locked_output_device: LockedOutputDevice<'_>) {
match TERMINAL_LIB_BACKEND {
TerminalLibBackend::Crossterm => {
RenderOpImplCrossterm {}.flush(locked_output_device);
}
TerminalLibBackend::Termion => unimplemented!(),
}
}
fn clear_before_flush(&mut self, locked_output_device: LockedOutputDevice<'_>) {
match TERMINAL_LIB_BACKEND {
TerminalLibBackend::Crossterm => {
RenderOpImplCrossterm {}.clear_before_flush(locked_output_device);
}
TerminalLibBackend::Termion => unimplemented!(),
}
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum FlushKind {
JustFlush,
ClearBeforeFlush,
}
pub trait Flush {
fn flush(&mut self, locked_output_device: LockedOutputDevice<'_>);
fn clear_before_flush(&mut self, locked_output_device: LockedOutputDevice<'_>);
}
pub trait DebugFormatRenderOp {
fn fmt_debug(&self, this: &RenderOp, f: &mut Formatter<'_>) -> Result;
}