//! Provide higher-level abstraction to draw things on backends. use backend::Backend; use enumset::EnumSet; use std::cmp::min; use theme::{BorderStyle, ColorStyle, Effect, PaletteColor, Style, Theme}; use unicode_segmentation::UnicodeSegmentation; use unicode_width::UnicodeWidthStr; use utils::lines::simple::prefix; use vec::Vec2; use with::With; /// Convenient interface to draw on a subset of the screen. /// /// The area it can print on is defined by `offset` and `size`. /// /// The part of the content it will print there is defined by `content_offset` /// and `size`. pub struct Printer<'a> { /// Offset into the window this printer should start drawing at. /// /// Printing at `x` will really print at `x + offset`. pub offset: Vec2, /// Size of the area we are allowed to draw on. /// /// Anything outside of this should be discarded. pub size: Vec2, /// Offset into the view for this printer. /// /// Printing at `x`, will really print at `x - content_offset`. pub content_offset: Vec2, /// Whether the view to draw is currently focused or not. pub focused: bool, /// Currently used theme pub theme: &'a Theme, /// Backend used to actually draw things backend: &'a Backend, } impl<'a> Clone for Printer<'a> { fn clone(&self) -> Self { Printer { offset: self.offset, content_offset: self.content_offset, size: self.size, focused: self.focused, theme: self.theme, backend: self.backend, } } } impl<'a> Printer<'a> { /// Creates a new printer on the given window. /// /// But nobody needs to know that. #[doc(hidden)] pub fn new>( size: T, theme: &'a Theme, backend: &'a Backend ) -> Self { Printer { offset: Vec2::zero(), content_offset: Vec2::zero(), size: size.into(), focused: true, theme, backend, } } /// Clear the screen. /// /// It will discard anything drawn before. /// /// Users rarely need to call this directly. pub fn clear(&self) { self.backend .clear(self.theme.palette[PaletteColor::Background]); } // TODO: use &mut self? We don't *need* it, but it may make sense. // We don't want people to start calling prints in parallel? /// Prints some text at the given position relative to the window. pub fn print>(&self, pos: S, text: &str) { let pos = pos.into(); if !pos.fits_in(self.size) { return; } // Do we have enough room for the entire line? let room = self.size.x - pos.x; // Drop the end of the text if it's too long // We want the number of CHARACTERS, not bytes. // (Actually we want the "width" of the string, see unicode-width) let prefix_len = prefix(text.graphemes(true), room, "").length; let text = &text[..prefix_len]; let pos = pos + self.offset; self.backend.print_at(pos, text); } /// Prints a vertical line using the given character. pub fn print_vline>(&self, start: T, len: usize, c: &str) { let start = start.into(); if !start.fits_in(self.size) { return; } let len = min(len, self.size.y - start.y); let start = start + self.offset; for y in 0..len { self.backend.print_at(start + (0,y), c); } } /// Prints a horizontal line using the given character. pub fn print_hline>(&self, start: T, len: usize, c: &str) { let start = start.into(); if !start.fits_in(self.size) { return; } let len = min(len, (self.size.x - start.x) / c.width()); let text: String = ::std::iter::repeat(c).take(len).collect(); let start = start + self.offset; self.backend.print_at(start, &text); } /// Call the given closure with a colored printer, /// that will apply the given color on prints. /// /// # Examples /// /// ```rust /// # use cursive::Printer; /// # use cursive::theme; /// # use cursive::backend; /// # let b = backend::dummy::Backend::init(); /// # let t = theme::load_default(); /// # let printer = Printer::new((6,4), &t, &*b); /// printer.with_color(theme::ColorStyle::highlight(), |printer| { /// printer.print((0,0), "This text is highlighted!"); /// }); /// ``` pub fn with_color(&self, c: ColorStyle, f: F) where F: FnOnce(&Printer), { let old = self.backend.set_color(c.resolve(&self.theme.palette)); f(self); self.backend.set_color(old); } /// Call the given closure with a styled printer, /// that will apply the given style on prints. pub fn with_style(&self, style: T, f: F) where F: FnOnce(&Printer), T: Into