printer: add doc

This commit is contained in:
Alexandre Bury 2018-04-24 17:19:07 -07:00
parent 8c2ff6bd4f
commit 5586403080

View File

@ -14,12 +14,12 @@ use with::With;
/// ///
/// The area it can print on is defined by `offset` and `size`. /// 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` /// The part of the content it will print is defined by `content_offset`
/// and `size`. /// and `size`.
pub struct Printer<'a> { pub struct Printer<'a> {
/// Offset into the window this printer should start drawing at. /// Offset into the window this printer should start drawing at.
/// ///
/// Printing at `x` will really print at `x + offset`. /// A print request at `x` will really print at `x + offset`.
pub offset: Vec2, pub offset: Vec2,
/// Size of the area we are allowed to draw on. /// Size of the area we are allowed to draw on.
@ -29,7 +29,7 @@ pub struct Printer<'a> {
/// Offset into the view for this printer. /// Offset into the view for this printer.
/// ///
/// Printing at `x`, will really print at `x - content_offset`. /// A print request `x`, will really print at `x - content_offset`.
pub content_offset: Vec2, pub content_offset: Vec2,
/// Whether the view to draw is currently focused or not. /// Whether the view to draw is currently focused or not.
@ -86,16 +86,19 @@ impl<'a> Printer<'a> {
// TODO: use &mut self? We don't *need* it, but it may make sense. // 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? // We don't want people to start calling prints in parallel?
/// Prints some text at the given position relative to the window. /// Prints some text at the given position relative to the window.
pub fn print<S: Into<Vec2>>(&self, pos: S, text: &str) { pub fn print<S: Into<Vec2>>(&self, start: S, text: &str) {
let pos = pos.into(); let start = start.into();
if !pos.fits_in(self.size + self.content_offset) { // We accept requests between `content_offset` and
// `content_offset + size`
if !start.fits_in(self.size + self.content_offset) {
return; return;
} }
// This is the part of the text that's hidden // If start < content_offset, part of the text will not be visible.
// (smaller than the content offset) // This is the part of the text that's hidden:
let hidden_part = self.content_offset.saturating_sub(pos); // (It should always be smaller than the content offset)
let hidden_part = self.content_offset.saturating_sub(start);
if hidden_part.y > 0 { if hidden_part.y > 0 {
// Since we are printing a single line, there's nothing we can do. // Since we are printing a single line, there's nothing we can do.
return; return;
@ -112,6 +115,7 @@ impl<'a> Printer<'a> {
let tail = suffix(text.graphemes(true), text_width - hidden_part.x, ""); let tail = suffix(text.graphemes(true), text_width - hidden_part.x, "");
let skipped_len = text.len() - tail.length; let skipped_len = text.len() - tail.length;
let skipped_width = text_width - tail.width; let skipped_width = text_width - tail.width;
assert_eq!(text[..skipped_len].width(), skipped_width);
// This should be equal most of the time, except when there's a double // This should be equal most of the time, except when there's a double
// character preventing us from splitting perfectly. // character preventing us from splitting perfectly.
@ -119,80 +123,90 @@ impl<'a> Printer<'a> {
// Drop part of the text, and move the cursor correspondingly. // Drop part of the text, and move the cursor correspondingly.
let text = &text[skipped_len..]; let text = &text[skipped_len..];
let pos = pos + (skipped_width, 0); let start = start + (skipped_width, 0);
assert!(start.fits(self.content_offset));
// What we did before should guarantee that this won't overflow. // What we did before should guarantee that this won't overflow.
let pos = pos - self.content_offset; let start = start - self.content_offset;
// Do we have enough room for the entire line? // Do we have enough room for the entire line?
let room = self.size.x - pos.x; let room = self.size.x - start.x;
// Drop the end of the text if it's too long // Drop the end of the text if it's too long
// We want the number of CHARACTERS, not bytes. // We want the number of CHARACTERS, not bytes.
// (Actually we want the "width" of the string, see unicode-width) // (Actually we want the "width" of the string, see unicode-width)
let prefix_len = prefix(text.graphemes(true), room, "").length; let prefix_len = prefix(text.graphemes(true), room, "").length;
let text = &text[..prefix_len]; let text = &text[..prefix_len];
assert!(text.width() <= room);
let pos = pos + self.offset; let start = start + self.offset;
self.backend.print_at(pos, text); self.backend.print_at(start, text);
} }
/// Prints a vertical line using the given character. /// Prints a vertical line using the given character.
pub fn print_vline<T: Into<Vec2>>(&self, start: T, len: usize, c: &str) { pub fn print_vline<T: Into<Vec2>>(&self, start: T, height: usize, c: &str) {
let start = start.into(); let start = start.into();
// Here again, we can abort if we're trying to print too far right or
// too low.
if !start.fits_in(self.size + self.content_offset) { if !start.fits_in(self.size + self.content_offset) {
return; return;
} }
// hidden_part describes how far to the top left of the viewport we are.
let hidden_part = self.content_offset.saturating_sub(start); let hidden_part = self.content_offset.saturating_sub(start);
if hidden_part.x > 0 || hidden_part.y >= len { if hidden_part.x > 0 || hidden_part.y >= height {
// We're printing a single column, so we can't do much here. // We're printing a single column, so we can't do much here.
return; return;
} }
// Skip `hidden_part` // Skip `hidden_part`
let start = start + hidden_part; let start = start + hidden_part;
let len = len - hidden_part.y; assert!(start.fits(self.content_offset));
let height = height - hidden_part.y;
// What we did before ensures this won't overflow. // What we did before ensures this won't overflow.
let start = start - self.content_offset; let start = start - self.content_offset;
// Don't go overboard // Don't go overboard
let len = min(len, self.size.y - start.y); let height = min(height, self.size.y - start.y);
let start = start + self.offset; let start = start + self.offset;
for y in 0..len { for y in 0..height {
self.backend.print_at(start + (0,y), c); self.backend.print_at(start + (0,y), c);
} }
} }
/// Prints a horizontal line using the given character. /// Prints a horizontal line using the given character.
pub fn print_hline<T: Into<Vec2>>(&self, start: T, len: usize, c: &str) { pub fn print_hline<T: Into<Vec2>>(&self, start: T, width: usize, c: &str) {
let start = start.into(); let start = start.into();
// Nothing to be done if the start if too far to the bottom/right
if !start.fits_in(self.size + self.content_offset) { if !start.fits_in(self.size + self.content_offset) {
return; return;
} }
let hidden_part = self.content_offset.saturating_sub(start); let hidden_part = self.content_offset.saturating_sub(start);
if hidden_part.y > 0 || hidden_part.x >= len { if hidden_part.y > 0 || hidden_part.x >= width {
// We're printing a single line, so we can't do much here. // We're printing a single line, so we can't do much here.
return; return;
} }
// Skip `hidden_part` // Skip `hidden_part`
let start = start + hidden_part; let start = start + hidden_part;
let len = len - hidden_part.x; assert!(start.fits(self.content_offset));
// Don't go overboard let width = width - hidden_part.x;
// Don't go too far
let start = start - self.content_offset; let start = start - self.content_offset;
// Don't write too much if we're close to the end // Don't write too much if we're close to the end
let len = min(len, (self.size.x - start.x) / c.width()); let width = min(width, (self.size.x - start.x) / c.width());
// Could we avoid allocating? // Could we avoid allocating?
let text: String = ::std::iter::repeat(c).take(len).collect(); let text: String = ::std::iter::repeat(c).take(width).collect();
let start = start + self.offset; let start = start + self.offset;
self.backend.print_at(start, &text); self.backend.print_at(start, &text);
@ -396,16 +410,23 @@ impl<'a> Printer<'a> {
} }
/// Returns a sub-printer with the given offset. /// Returns a sub-printer with the given offset.
///
/// It will print in an area slightly to the bottom/right.
pub fn offset<S>(&self, offset: S) -> Printer pub fn offset<S>(&self, offset: S) -> Printer
where where
S: Into<Vec2>, S: Into<Vec2>,
{ {
let offset = offset.into(); let offset = offset.into();
self.clone().with(|s| { self.clone().with(|s| {
// If we are drawing a part of the content,
// let's reduce this first.
let consumed = Vec2::min(s.content_offset, offset); let consumed = Vec2::min(s.content_offset, offset);
let offset = offset - consumed;
s.content_offset = s.content_offset - consumed; s.content_offset = s.content_offset - consumed;
s.offset = s.offset + offset - consumed;
s.offset = s.offset + offset;
s.size = s.size.saturating_sub(offset); s.size = s.size.saturating_sub(offset);
}) })
} }