cursive/src/printer.rs

180 lines
5.7 KiB
Rust
Raw Normal View History

2015-05-15 19:16:58 +00:00
//! Makes drawing on ncurses windows easier.
use std::cmp::min;
use backend::Backend;
2015-05-22 06:29:49 +00:00
use B;
2016-07-10 02:05:51 +00:00
use theme::{ColorStyle, Effect, Theme};
use vec::Vec2;
/// Convenient interface to draw on a subset of the screen.
#[derive(Clone)]
pub struct Printer {
/// Offset into the window this printer should start drawing at.
pub offset: Vec2,
/// Size of the area we are allowed to draw on.
pub size: Vec2,
/// Whether the view to draw is currently focused or not.
pub focused: bool,
/// Currently used theme
pub theme: Theme,
}
impl Printer {
2015-05-22 06:29:49 +00:00
/// Creates a new printer on the given window.
pub fn new<T: Into<Vec2>>(size: T, theme: Theme) -> Self {
2015-05-22 06:29:49 +00:00
Printer {
offset: Vec2::zero(),
size: size.into(),
focused: true,
theme: theme,
2015-05-22 06:29:49 +00:00
}
}
2016-06-28 05:10:59 +00:00
// 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<S: Into<Vec2>>(&self, pos: S, text: &str) {
let p = pos.into();
2016-03-15 22:37:57 +00:00
if p.y >= self.size.y || p.x >= self.size.x {
return;
}
// Do we have enough room for the entire line?
let room = self.size.x - p.x;
// We want the number of CHARACTERS, not bytes.
2016-06-28 05:10:59 +00:00
// (Actually we want the "width" of the string, see unicode-width)
let text = match text.char_indices().nth(room) {
2016-03-15 22:37:57 +00:00
Some((i, _)) => &text[..i],
_ => text,
};
2015-05-29 00:37:00 +00:00
let p = p + self.offset;
2016-06-28 05:40:11 +00:00
if text.contains('%') {
B::print_at((p.x, p.y), &text.replace("%", "%%"));
2015-05-29 00:37:00 +00:00
} else {
B::print_at((p.x, p.y), text);
2015-05-29 00:37:00 +00:00
}
}
/// Prints a vertical line using the given character.
pub fn print_vline<T: Into<Vec2>>(&self, start: T, len: usize, c: &str) {
let p = start.into();
2016-03-15 22:37:57 +00:00
if p.y > self.size.y || p.x > self.size.x {
return;
}
let len = min(len, self.size.y - p.y);
let p = p + self.offset;
for y in 0..len {
B::print_at((p.x, (p.y + y)), c);
}
}
/// Prints a horizontal line using the given character.
pub fn print_hline<T: Into<Vec2>>(&self, start: T, len: usize, c: &str) {
let p = start.into();
2016-03-15 22:37:57 +00:00
if p.y > self.size.y || p.x > self.size.x {
return;
}
let len = min(len, self.size.x - p.x);
let p = p + self.offset;
for x in 0..len {
B::print_at((p.x + x, p.y), c);
}
2015-05-22 06:29:49 +00:00
}
2015-05-27 07:09:22 +00:00
/// Call the given closure with a colored printer,
/// that will apply the given color on prints.
2015-05-26 23:48:27 +00:00
///
/// # Examples
///
/// ```no_run
2015-06-04 18:40:35 +00:00
/// # use cursive::printer::Printer;
/// # use cursive::theme;
/// # let printer = Printer::new((6,4), theme::load_default());
/// printer.with_color(theme::ColorStyle::Highlight, |printer| {
2015-05-26 23:48:27 +00:00
/// printer.print((0,0), "This text is highlighted!");
/// });
/// ```
pub fn with_color<F>(&self, c: ColorStyle, f: F)
where F: FnOnce(&Printer)
{
B::with_color(c, || f(self));
}
2015-05-27 07:09:22 +00:00
/// Same as `with_color`, but apply a ncurses style instead,
/// like `ncurses::A_BOLD()` or `ncurses::A_REVERSE()`.
///
/// Will probably use a cursive enum some day.
pub fn with_effect<F>(&self, effect: Effect, f: F)
where F: FnOnce(&Printer)
{
B::with_effect(effect, || f(self));
}
2015-05-20 17:36:35 +00:00
/// Prints a rectangular box.
///
/// # Examples
///
/// ```
2015-06-04 18:40:35 +00:00
/// # use cursive::printer::Printer;
/// # use cursive::theme;
/// # let printer = Printer::new((6,4), theme::load_default());
2015-05-26 23:48:27 +00:00
/// printer.print_box((0,0), (6,4));
2015-05-20 17:36:35 +00:00
/// ```
pub fn print_box<T: Into<Vec2>>(&self, start: T, size: T) {
let start_v = start.into();
let size_v = size.into() - (1, 1);
2015-05-22 06:29:49 +00:00
self.print(start_v, "");
self.print(start_v + size_v.keep_x(), "");
self.print(start_v + size_v.keep_y(), "");
self.print(start_v + size_v, "");
self.print_hline(start_v + (1, 0), size_v.x - 1, "");
self.print_vline(start_v + (0, 1), size_v.y - 1, "");
2016-07-10 02:05:51 +00:00
self.print_hline(start_v + (1, 0) + size_v.keep_y(),
size_v.x - 1,
"");
self.print_vline(start_v + (0, 1) + size_v.keep_x(),
size_v.y - 1,
"");
2016-06-28 05:10:59 +00:00
}
2016-07-02 08:01:09 +00:00
pub fn with_selection<F: FnOnce(&Printer)>(&self, selection: bool, f: F) {
self.with_color(if selection {
2016-07-10 02:05:51 +00:00
if self.focused {
ColorStyle::Highlight
} else {
ColorStyle::HighlightInactive
}
} else {
ColorStyle::Primary
},
f);
2016-07-02 08:01:09 +00:00
}
pub fn print_hdelim<T: Into<Vec2>>(&self, start: T, len: usize) {
let start = start.into();
2016-06-28 05:10:59 +00:00
self.print(start, "");
self.print_hline(start + (1, 0), len - 2, "");
self.print(start + (len - 1, 0), "");
}
/// Returns a printer on a subset of this one's area.
pub fn sub_printer<S: Into<Vec2>>(&self, offset: S, size: S, focused: bool)
-> Printer {
let offset_v = offset.into();
Printer {
offset: self.offset + offset_v,
// We can't be larger than what remains
size: Vec2::min(self.size - offset_v, size.into()),
focused: self.focused && focused,
theme: self.theme.clone(),
}
}
}