2015-05-15 19:16:58 +00:00
|
|
|
//! Makes drawing on ncurses windows easier.
|
|
|
|
|
2015-05-24 08:49:50 +00:00
|
|
|
use std::cmp::min;
|
|
|
|
|
2015-05-15 18:58:47 +00:00
|
|
|
use ncurses;
|
2015-05-22 06:29:49 +00:00
|
|
|
|
2016-03-15 22:37:57 +00:00
|
|
|
use theme::{ColorPair, Theme};
|
2016-06-28 05:10:59 +00:00
|
|
|
use vec::{ToVec2, Vec2};
|
2015-05-15 18:58:47 +00:00
|
|
|
|
2015-05-23 22:58:06 +00:00
|
|
|
/// Convenient interface to draw on a subset of the screen.
|
2015-05-15 18:58:47 +00:00
|
|
|
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,
|
2015-05-31 04:05:34 +00:00
|
|
|
/// Whether the view to draw is currently focused or not.
|
|
|
|
pub focused: bool,
|
2015-06-06 01:08:05 +00:00
|
|
|
/// Currently used theme
|
|
|
|
pub theme: Theme,
|
2015-05-15 18:58:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Printer {
|
2015-05-22 06:29:49 +00:00
|
|
|
/// Creates a new printer on the given window.
|
2015-06-06 01:08:05 +00:00
|
|
|
pub fn new<T: ToVec2>(size: T, theme: Theme) -> Self {
|
2015-05-22 06:29:49 +00:00
|
|
|
Printer {
|
|
|
|
offset: Vec2::zero(),
|
|
|
|
size: size.to_vec2(),
|
2015-05-31 04:05:34 +00:00
|
|
|
focused: true,
|
2015-06-06 01:08:05 +00:00
|
|
|
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?
|
2015-05-15 18:58:47 +00:00
|
|
|
/// Prints some text at the given position relative to the window.
|
|
|
|
pub fn print<S: ToVec2>(&self, pos: S, text: &str) {
|
2015-05-24 08:49:50 +00:00
|
|
|
let p = pos.to_vec2();
|
2016-03-15 22:37:57 +00:00
|
|
|
if p.y >= self.size.y || p.x >= self.size.x {
|
|
|
|
return;
|
|
|
|
}
|
2015-05-24 08:49:50 +00:00
|
|
|
// Do we have enough room for the entire line?
|
2015-05-25 21:46:29 +00:00
|
|
|
let room = self.size.x - p.x;
|
2015-05-24 08:49:50 +00:00
|
|
|
// 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)
|
2015-05-24 08:49:50 +00:00
|
|
|
let text = match text.char_indices().nth(room) {
|
2016-03-15 22:37:57 +00:00
|
|
|
Some((i, _)) => &text[..i],
|
2015-05-24 08:49:50 +00:00
|
|
|
_ => text,
|
|
|
|
};
|
2015-05-29 00:37:00 +00:00
|
|
|
|
2015-05-24 08:49:50 +00:00
|
|
|
let p = p + self.offset;
|
2015-05-29 00:37:00 +00:00
|
|
|
if text.contains("%") {
|
2016-06-28 05:10:59 +00:00
|
|
|
ncurses::mvprintw(p.y as i32,
|
|
|
|
p.x as i32,
|
|
|
|
&text.replace("%", "%%"));
|
2015-05-29 00:37:00 +00:00
|
|
|
} else {
|
|
|
|
ncurses::mvprintw(p.y as i32, p.x as i32, text);
|
|
|
|
}
|
2015-05-15 18:58:47 +00:00
|
|
|
}
|
|
|
|
|
2015-05-20 17:31:38 +00:00
|
|
|
/// Prints a vertical line using the given character.
|
2016-06-26 22:03:12 +00:00
|
|
|
pub fn print_vline<T: ToVec2>(&self, start: T, len: usize, c: &str) {
|
2015-05-24 08:49:50 +00:00
|
|
|
let p = start.to_vec2();
|
2016-03-15 22:37:57 +00:00
|
|
|
if p.y > self.size.y || p.x > self.size.x {
|
|
|
|
return;
|
|
|
|
}
|
2015-05-24 08:49:50 +00:00
|
|
|
let len = min(len, self.size.y - p.y);
|
|
|
|
|
|
|
|
let p = p + self.offset;
|
2016-06-25 22:52:19 +00:00
|
|
|
for y in 0..len {
|
2016-06-26 22:03:12 +00:00
|
|
|
ncurses::mvaddstr((p.y + y) as i32, p.x as i32, c);
|
2016-06-25 22:52:19 +00:00
|
|
|
}
|
2015-05-20 17:31:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Prints a horizontal line using the given character.
|
2016-06-26 22:03:12 +00:00
|
|
|
pub fn print_hline<T: ToVec2>(&self, start: T, len: usize, c: &str) {
|
2015-05-24 08:49:50 +00:00
|
|
|
let p = start.to_vec2();
|
2016-03-15 22:37:57 +00:00
|
|
|
if p.y > self.size.y || p.x > self.size.x {
|
|
|
|
return;
|
|
|
|
}
|
2015-05-24 08:49:50 +00:00
|
|
|
let len = min(len, self.size.x - p.x);
|
|
|
|
|
|
|
|
let p = p + self.offset;
|
2016-06-26 22:03:12 +00:00
|
|
|
for x in 0..len {
|
|
|
|
ncurses::mvaddstr(p.y as i32, (p.x + x) as i32, 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
|
|
|
|
///
|
|
|
|
/// ```
|
2015-06-04 18:40:35 +00:00
|
|
|
/// # use cursive::printer::Printer;
|
2015-06-06 01:08:05 +00:00
|
|
|
/// # use cursive::theme;
|
|
|
|
/// # let printer = Printer::new((6,4), theme::load_default());
|
|
|
|
/// printer.with_color(theme::ColorPair::Highlight, |printer| {
|
2015-05-26 23:48:27 +00:00
|
|
|
/// printer.print((0,0), "This text is highlighted!");
|
|
|
|
/// });
|
|
|
|
/// ```
|
2015-06-06 01:08:05 +00:00
|
|
|
pub fn with_color<'a, F>(&'a self, c: ColorPair, f: F)
|
2015-05-23 22:58:06 +00:00
|
|
|
where F: Fn(&Printer)
|
|
|
|
{
|
2015-06-06 01:08:05 +00:00
|
|
|
self.with_style(ncurses::COLOR_PAIR(c.ncurses_id()), f);
|
|
|
|
ncurses::attron(ncurses::COLOR_PAIR(ColorPair::Primary.ncurses_id()));
|
2015-05-20 17:31:38 +00:00
|
|
|
}
|
|
|
|
|
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.
|
2015-05-27 04:45:00 +00:00
|
|
|
pub fn with_style<'a, F>(&'a self, style: ncurses::attr_t, f: F)
|
|
|
|
where F: Fn(&Printer)
|
|
|
|
{
|
|
|
|
ncurses::attron(style);
|
|
|
|
f(self);
|
|
|
|
ncurses::attroff(style);
|
|
|
|
}
|
|
|
|
|
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;
|
2015-06-06 01:08:05 +00:00
|
|
|
/// # 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
|
|
|
/// ```
|
2015-05-22 06:29:49 +00:00
|
|
|
pub fn print_box<T: ToVec2>(&self, start: T, size: T) {
|
2015-05-20 17:31:38 +00:00
|
|
|
let start_v = start.to_vec2();
|
2016-03-15 22:37:57 +00:00
|
|
|
let size_v = size.to_vec2() - (1, 1);
|
2015-05-20 17:31:38 +00:00
|
|
|
|
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, "┘");
|
|
|
|
|
2016-06-26 22:03:12 +00:00
|
|
|
self.print_hline(start_v + (1, 0), size_v.x - 1, "─");
|
|
|
|
self.print_vline(start_v + (0, 1), size_v.y - 1, "│");
|
2016-06-28 05:10:59 +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,
|
|
|
|
"│");
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn print_hdelim<T: ToVec2>(&self, start: T, len: usize) {
|
|
|
|
let start = start.to_vec2();
|
|
|
|
self.print(start, "├");
|
|
|
|
self.print_hline(start + (1, 0), len - 2, "─");
|
|
|
|
self.print(start + (len - 1, 0), "┤");
|
2015-05-20 17:31:38 +00:00
|
|
|
}
|
|
|
|
|
2015-05-15 18:58:47 +00:00
|
|
|
/// Returns a printer on a subset of this one's area.
|
2016-06-28 05:10:59 +00:00
|
|
|
pub fn sub_printer<S: ToVec2>(&self, offset: S, size: S, focused: bool)
|
|
|
|
-> Printer {
|
2015-05-15 18:58:47 +00:00
|
|
|
let offset_v = offset.to_vec2();
|
|
|
|
Printer {
|
|
|
|
offset: self.offset + offset_v,
|
|
|
|
// We can't be larger than what remains
|
|
|
|
size: Vec2::min(self.size - offset_v, size.to_vec2()),
|
2015-05-31 04:05:34 +00:00
|
|
|
focused: self.focused && focused,
|
2015-06-06 01:08:05 +00:00
|
|
|
theme: self.theme.clone(),
|
2015-05-15 18:58:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|