use std::fmt::Debug;
use r3bl_rs_utils_core::*;
use r3bl_rs_utils_macro::style;
use super::*;
use crate::*;
const DEFAULT_CURSOR_CHAR: char = '▒';
pub struct EditorEngineRenderApi;
impl EditorEngineRenderApi {
pub async fn apply_event<S, A>(
args: EditorEngineArgs<'_, S, A>,
input_event: &InputEvent,
) -> CommonResult<EditorEngineApplyResponse<EditorBuffer>>
where
S: Default + Clone + PartialEq + Debug + Sync + Send,
A: Default + Clone + Sync + Send,
{
let EditorEngineArgs {
editor_buffer,
component_registry,
shared_tw_data,
self_id,
editor_engine,
..
} = args;
if let Ok(editor_event) = EditorEvent::try_from(input_event) {
let mut new_editor_buffer = editor_buffer.clone();
EditorEvent::apply_editor_event(
editor_engine,
&mut new_editor_buffer,
editor_event,
shared_tw_data,
component_registry,
self_id,
);
Ok(EditorEngineApplyResponse::Applied(new_editor_buffer))
} else {
Ok(EditorEngineApplyResponse::NotApplied)
}
}
pub async fn render_engine<S, A>(
args: EditorEngineArgs<'_, S, A>,
current_box: &FlexBox,
) -> CommonResult<RenderPipeline>
where
S: Default + Clone + PartialEq + Debug + Sync + Send,
A: Default + Clone + Sync + Send,
{
throws_with_return!({
let EditorEngineArgs {
editor_buffer,
component_registry,
editor_engine,
..
} = args;
editor_engine.current_box = current_box.into();
let render_args = RenderArgs {
editor_buffer,
component_registry,
editor_engine,
};
if editor_buffer.is_empty() {
EditorEngineRenderApi::render_empty_state(&render_args)
} else {
let mut render_ops = render_ops!();
EditorEngineRenderApi::render_content(&render_args, &mut render_ops);
EditorEngineRenderApi::render_caret(CaretPaintStyle::LocalPaintedEffect, &render_args, &mut render_ops);
let mut render_pipeline = render_pipeline!();
render_pipeline.push(ZOrder::Normal, render_ops);
render_pipeline
}
})
}
fn render_content<S, A>(render_args: &RenderArgs<'_, S, A>, render_ops: &mut RenderOps)
where
S: Default + Clone + PartialEq + Debug + Sync + Send,
A: Default + Clone + Sync + Send,
{
let RenderArgs {
editor_buffer,
editor_engine,
..
} = render_args;
let Size {
cols: max_display_col_count,
rows: max_display_row_count,
} = editor_engine.current_box.style_adjusted_bounds_size;
for (row_index, line) in editor_buffer
.get_lines()
.iter()
.skip(ch!(@to_usize editor_buffer.get_scroll_offset().row))
.enumerate()
{
if ch!(row_index) > max_display_row_count {
break;
}
let truncated_line = line.truncate_start_by_n_col(editor_buffer.get_scroll_offset().col);
let truncated_line = UnicodeString::from(truncated_line);
let truncated_line = truncated_line.truncate_end_to_fit_display_cols(max_display_col_count);
render_ops.push(RenderOp::MoveCursorPositionRelTo(
editor_engine.current_box.style_adjusted_origin_pos,
position! { col: 0 , row: ch!(@to_usize row_index) },
));
render_ops.push(RenderOp::ApplyColors(editor_engine.current_box.get_computed_style()));
render_ops.push(RenderOp::PrintTextWithAttributesAndPadding(
truncated_line.into(),
editor_engine.current_box.get_computed_style(),
max_display_col_count,
));
render_ops.push(RenderOp::ResetColor);
}
}
fn render_caret<S, A>(style: CaretPaintStyle, render_args: &RenderArgs<'_, S, A>, render_ops: &mut RenderOps)
where
S: Default + Clone + PartialEq + Debug + Sync + Send,
A: Default + Clone + Sync + Send,
{
let RenderArgs {
component_registry,
editor_buffer,
editor_engine,
..
} = render_args;
if component_registry
.has_focus
.does_id_have_focus(editor_engine.current_box.id)
{
match style {
CaretPaintStyle::GlobalCursor => {
render_ops.push(RenderOp::RequestShowCaretAtPositionRelTo(
editor_engine.current_box.style_adjusted_origin_pos,
editor_buffer.get_caret(CaretKind::Raw),
));
}
CaretPaintStyle::LocalPaintedEffect => {
let str_at_caret: String = if let Some(UnicodeStringSegmentSliceResult {
unicode_string_seg: str_seg,
..
}) = EditorEngineInternalApi::string_at_caret(editor_buffer, editor_engine)
{
str_seg.string
} else {
DEFAULT_CURSOR_CHAR.into()
};
render_ops.push(RenderOp::MoveCursorPositionRelTo(
editor_engine.current_box.style_adjusted_origin_pos,
editor_buffer.get_caret(CaretKind::Raw),
));
render_ops.push(RenderOp::PrintTextWithAttributes(
str_at_caret,
style! { attrib: [reverse] }.into(),
));
render_ops.push(RenderOp::MoveCursorPositionRelTo(
editor_engine.current_box.style_adjusted_origin_pos,
editor_buffer.get_caret(CaretKind::Raw),
));
render_ops.push(RenderOp::ResetColor);
}
}
}
}
pub fn render_empty_state<S, A>(render_args: &RenderArgs<'_, S, A>) -> RenderPipeline
where
S: Default + Clone + PartialEq + Debug + Sync + Send,
A: Default + Clone + Sync + Send,
{
let RenderArgs {
component_registry,
editor_engine,
..
} = render_args;
let mut pipeline = render_pipeline!();
let mut content_cursor_pos = position! { col: 0 , row: 0 };
render_pipeline! {
@push_into pipeline
at ZOrder::Normal
=>
RenderOp::MoveCursorPositionRelTo(
editor_engine.current_box.style_adjusted_origin_pos, position! { col: 0 , row: 0 }),
RenderOp::ApplyColors(style! {
color_fg: TWColor::Red
}.into()),
RenderOp::PrintTextWithAttributes("No content added".into(), None),
RenderOp::ResetColor
};
if component_registry
.has_focus
.does_id_have_focus(editor_engine.current_box.id)
{
render_pipeline! {
@push_into pipeline
at ZOrder::Normal
=>
RenderOp::MoveCursorPositionRelTo(
editor_engine.current_box.style_adjusted_origin_pos,
content_cursor_pos.add_row_with_bounds(
ch!(1), editor_engine.current_box.style_adjusted_bounds_size.rows)),
RenderOp::PrintTextWithAttributes("👀".into(), None),
RenderOp::ResetColor
};
}
pipeline
}
}
mod misc {
use super::*;
#[derive(Debug)]
pub(super) enum CaretPaintStyle {
#[allow(dead_code)]
GlobalCursor,
LocalPaintedEffect,
}
pub enum EditorEngineApplyResponse<T>
where
T: Debug,
{
Applied(T),
NotApplied,
}
}
pub use misc::*;