diff options
Diffstat (limited to 'src/handlers/draw.rs')
-rw-r--r-- | src/handlers/draw.rs | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/src/handlers/draw.rs b/src/handlers/draw.rs new file mode 100644 index 00000000..d7edd8cd --- /dev/null +++ b/src/handlers/draw.rs @@ -0,0 +1,300 @@ +use std::cmp::max; +use std::io::Write; + +use crate::ansi; +use crate::cli::Width; +use crate::style::{DecorationStyle, Style}; + +pub type DrawFunction = + dyn FnMut(&mut dyn Write, &str, &str, &Width, Style, ansi_term::Style) -> std::io::Result<()>; + +pub fn get_draw_function( + decoration_style: DecorationStyle, +) -> (Box<DrawFunction>, bool, ansi_term::Style) { + match decoration_style { + DecorationStyle::Box(style) => (Box::new(write_boxed), true, style), + DecorationStyle::BoxWithUnderline(style) => { + (Box::new(write_boxed_with_underline), true, style) + } + DecorationStyle::BoxWithOverline(style) => { + // TODO: not implemented + (Box::new(write_boxed), true, style) + } + DecorationStyle::BoxWithUnderOverline(style) => { + // TODO: not implemented + (Box::new(write_boxed), true, style) + } + DecorationStyle::Underline(style) => (Box::new(write_underlined), false, style), + DecorationStyle::Overline(style) => (Box::new(write_overlined), false, style), + DecorationStyle::UnderOverline(style) => (Box::new(write_underoverlined), false, style), + DecorationStyle::NoDecoration => ( + Box::new(write_no_decoration), + false, + ansi_term::Style::new(), + ), + } +} + +fn write_no_decoration( + writer: &mut dyn Write, + text: &str, + raw_text: &str, + _line_width: &Width, // ignored + text_style: Style, + _decoration_style: ansi_term::Style, +) -> std::io::Result<()> { + if text_style.is_raw { + writeln!(writer, "{}", raw_text)?; + } else { + writeln!(writer, "{}", text_style.paint(text))?; + } + Ok(()) +} + +/// Write text to stream, surrounded by a box, leaving the cursor just +/// beyond the bottom right corner. +fn write_boxed( + writer: &mut dyn Write, + text: &str, + raw_text: &str, + _line_width: &Width, // ignored + text_style: Style, + decoration_style: ansi_term::Style, +) -> std::io::Result<()> { + let up_left = if decoration_style.is_bold { + box_drawing::heavy::UP_LEFT + } else { + box_drawing::light::UP_LEFT + }; + let box_width = ansi::measure_text_width(text); + write_boxed_partial( + writer, + text, + raw_text, + box_width, + text_style, + decoration_style, + )?; + writeln!(writer, "{}", decoration_style.paint(up_left))?; + Ok(()) +} + +/// Write text to stream, surrounded by a box, and extend a line from +/// the bottom right corner. +fn write_boxed_with_underline( + writer: &mut dyn Write, + text: &str, + raw_text: &str, + line_width: &Width, + text_style: Style, + decoration_style: ansi_term::Style, +) -> std::io::Result<()> { + let box_width = ansi::measure_text_width(text); + write_boxed_with_horizontal_whisker( + writer, + text, + raw_text, + box_width, + text_style, + decoration_style, + )?; + let line_width = match *line_width { + Width::Fixed(n) => n, + Width::Variable => box_width, + }; + write_horizontal_line( + writer, + if line_width > box_width { + line_width - box_width - 1 + } else { + 0 + }, + text_style, + decoration_style, + )?; + writeln!(writer)?; + Ok(()) +} + +enum UnderOverline { + Under, + Over, + Underover, +} + +fn write_underlined( + writer: &mut dyn Write, + text: &str, + raw_text: &str, + line_width: &Width, + text_style: Style, + decoration_style: ansi_term::Style, +) -> std::io::Result<()> { + _write_under_or_over_lined( + UnderOverline::Under, + writer, + text, + raw_text, + line_width, + text_style, + decoration_style, + ) +} + +fn write_overlined( + writer: &mut dyn Write, + text: &str, + raw_text: &str, + line_width: &Width, + text_style: Style, + decoration_style: ansi_term::Style, +) -> std::io::Result<()> { + _write_under_or_over_lined( + UnderOverline::Over, + writer, + text, + raw_text, + line_width, + text_style, + decoration_style, + ) +} + +fn write_underoverlined( + writer: &mut dyn Write, + text: &str, + raw_text: &str, + line_width: &Width, + text_style: Style, + decoration_style: ansi_term::Style, +) -> std::io::Result<()> { + _write_under_or_over_lined( + UnderOverline::Underover, + writer, + text, + raw_text, + line_width, + text_style, + decoration_style, + ) +} + +fn _write_under_or_over_lined( + underoverline: UnderOverline, + writer: &mut dyn Write, + text: &str, + raw_text: &str, + line_width: &Width, + text_style: Style, + decoration_style: ansi_term::Style, +) -> std::io::Result<()> { + let text_width = ansi::measure_text_width(text); + let line_width = match *line_width { + Width::Fixed(n) => max(n, text_width), + Width::Variable => text_width, + }; + let mut write_line: Box<dyn FnMut(&mut dyn Write) -> std::io::Result<()>> = + Box::new(|writer| { + write_horizontal_line(writer, line_width, text_style, decoration_style)?; + writeln!(writer)?; + Ok(()) + }); + match underoverline { + UnderOverline::Under => {} + _ => write_line(writer)?, + } + if text_style.is_raw { + writeln!(writer, "{}", raw_text)?; + } else { + writeln!(writer, "{}", text_style.paint(text))?; + } + match underoverline { + UnderOverline::Over => {} + _ => write_line(writer)?, + } + Ok(()) +} + +fn write_horizontal_line( + writer: &mut dyn Write, + width: usize, + _text_style: Style, + decoration_style: ansi_term::Style, +) -> std::io::Result<()> { + let horizontal = if decoration_style.is_bold { + box_drawing::heavy::HORIZONTAL + } else { + box_drawing::light::HORIZONTAL + }; + write!( + writer, + "{}", + decoration_style.paint(horizontal.repeat(width)) + ) +} + +fn write_boxed_with_horizontal_whisker( + writer: &mut dyn Write, + text: &str, + raw_text: &str, + box_width: usize, + text_style: Style, + decoration_style: ansi_term::Style, +) -> std::io::Result<()> { + let up_horizontal = if decoration_style.is_bold { + box_drawing::heavy::UP_HORIZONTAL + } else { + box_drawing::light::UP_HORIZONTAL + }; + write_boxed_partial( + writer, + text, + raw_text, + box_width, + text_style, + decoration_style, + )?; + write!(writer, "{}", decoration_style.paint(up_horizontal))?; + Ok(()) +} + +fn write_boxed_partial( + writer: &mut dyn Write, + text: &str, + raw_text: &str, + box_width: usize, + text_style: Style, + decoration_style: ansi_term::Style, +) -> std::io::Result<()> { + let (horizontal, down_left, vertical) = if decoration_style.is_bold { + ( + box_drawing::heavy::HORIZONTAL, + box_drawing::heavy::DOWN_LEFT, + box_drawing::heavy::VERTICAL, + ) + } else { + ( + box_drawing::light::HORIZONTAL, + box_drawing::light::DOWN_LEFT, + box_drawing::light::VERTICAL, + ) + }; + let horizontal_edge = horizontal.repeat(box_width); + writeln!( + writer, + "{}{}", + decoration_style.paint(&horizontal_edge), + decoration_style.paint(down_left), + )?; + if text_style.is_raw { + write!(writer, "{}", raw_text)?; + } else { + write!(writer, "{}", text_style.paint(text))?; + } + write!( + writer, + "{}\n{}", + decoration_style.paint(vertical), + decoration_style.paint(&horizontal_edge), + ) +} |