diff --git a/src/cursive.rs b/src/cursive.rs index 1117d89..18153e4 100644 --- a/src/cursive.rs +++ b/src/cursive.rs @@ -537,7 +537,7 @@ impl Cursive { 1 }; let id = self.active_screen; - let sv_printer = printer.offset((0, offset), !selected); + let sv_printer = printer.offset((0, offset)).focused(!selected); self.screens[id].draw_bg(&sv_printer); @@ -545,11 +545,7 @@ impl Cursive { // If the menubar is active, nothing else can be. // Draw the menubar? if self.menubar.visible() { - let printer = printer.sub_printer( - Vec2::zero(), - printer.size, - self.menubar.receive_events(), - ); + let printer = printer.focused(self.menubar.receive_events()); self.menubar.draw(&printer); } diff --git a/src/printer.rs b/src/printer.rs index a74e78e..ea12cfe 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -9,6 +9,7 @@ use theme::{BorderStyle, ColorStyle, Effect, PaletteColor, Style, Theme}; use unicode_segmentation::UnicodeSegmentation; use utils::lines::simple::prefix; use vec::Vec2; +use with::With; /// Convenient interface to draw on a subset of the screen. pub struct Printer<'a> { @@ -36,6 +37,20 @@ pub struct Printer<'a> { backend: &'a backend::Concrete, } +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, + new: Rc::clone(&self.new), + } + } +} + impl<'a> Printer<'a> { /// Creates a new printer on the given window. /// @@ -308,37 +323,73 @@ impl<'a> Printer<'a> { } /// Prints a horizontal delimiter with side border `├` and `┤`. - pub fn print_hdelim>(&self, start: T, len: usize) { + pub fn print_hdelim(&self, start: T, len: usize) + where + T: Into, + { let start = start.into(); self.print(start, "├"); self.print_hline(start + (1, 0), len.saturating_sub(2), "─"); self.print(start + (len.saturating_sub(1), 0), "┤"); } - /// Returns a printer on a subset of this one's area. - pub fn sub_printer, T: Into>( - &'a self, offset: S, size: T, focused: bool - ) -> Printer<'a> { - let size = size.into(); - let offset = offset.into().or_min(self.size); - let available = if !offset.fits_in(self.size) { - Vec2::zero() - } else { - Vec2::min(self.size - offset, size) - }; - Printer { - offset: self.offset + offset, - // We can't be larger than what remains - size: available, - focused: self.focused && focused, - theme: self.theme, - backend: self.backend, - new: Rc::clone(&self.new), - } + /// Returns a sub-printer with the given offset. + pub fn offset(&self, offset: S) -> Printer + where + S: Into, + { + let offset = offset.into(); + self.clone().with(|s| { + let consumed = Vec2::min(s.content_offset, offset); + + s.content_offset = s.content_offset - consumed; + s.offset = s.offset + offset - consumed; + s.size = s.size.saturating_sub(offset); + }) } - /// Returns a sub-printer with the given offset. - pub fn offset>(&self, offset: S, focused: bool) -> Printer { - self.sub_printer(offset, self.size, focused) + /// Returns a new sub-printer inheriting the given focus. + /// + /// If `self` is focused and `focused == true`, the child will be focused. + /// + /// Otherwise, he will be unfocused. + pub fn focused(&self, focused: bool) -> Self { + self.clone().with(|s| { + s.focused &= focused; + }) + } + + /// Returns a new sub-printer with a cropped area. + /// + /// The new printer size will be the minimum of `size` and its current size. + /// + /// Any size reduction happens at the bottom-right. + pub fn cropped(&self, size: S) -> Self + where + S: Into, + { + self.clone().with(|s| { + s.size = Vec2::min(s.size, size); + }) + } + + /// Returns a new sub-printer with a shrinked area. + /// + /// The printer size will be reduced by the given border from the bottom-right. + pub fn shrinked(&self, borders: S) -> Self + where + S: Into, + { + self.cropped(self.size.saturating_sub(borders)) + } + + /// Returns a new sub-printer with a content offset. + pub fn content_offset(&self, offset: S) -> Self + where + S: Into, + { + self.clone().with(|s| { + s.content_offset = s.content_offset + offset; + }) } } diff --git a/src/view/scroll.rs b/src/view/scroll.rs index 3ec10de..797763b 100644 --- a/src/view/scroll.rs +++ b/src/view/scroll.rs @@ -269,7 +269,7 @@ impl ScrollBase { // Y is the actual coordinate of the line. // The item ID is then Y + self.start_line line_drawer( - &printer.sub_printer(Vec2::new(0, y), Vec2::new(w, 1), true), + &printer.offset((0, y)).cropped((w, 1)), y + self.start_line, ); } diff --git a/src/views/dialog.rs b/src/views/dialog.rs index b4d945d..2b53987 100644 --- a/src/views/dialog.rs +++ b/src/views/dialog.rs @@ -371,11 +371,10 @@ impl Dialog { // Add some special effect to the focused button let position = Vec2::new(offset, y); button.offset.set(position); - button.button.draw(&printer.sub_printer( - position, - size, - self.focus == DialogFocus::Button(i), - )); + button.button.draw(&printer + .offset(position) + .cropped(size) + .focused(self.focus == DialogFocus::Button(i))); // Keep 1 blank between two buttons offset += size.x + 1; // Also keep 1 blank above the buttons @@ -395,11 +394,10 @@ impl Dialog { None => return, }; - self.content.draw(&printer.sub_printer( - self.borders.top_left() + self.padding.top_left(), - inner_size, - self.focus == DialogFocus::Content, - )); + self.content.draw(&printer + .offset(self.borders.top_left() + self.padding.top_left()) + .cropped(inner_size) + .focused(self.focus == DialogFocus::Content)); } fn draw_title(&self, printer: &Printer) { diff --git a/src/views/linear_layout.rs b/src/views/linear_layout.rs index 2766ee6..6b516e1 100644 --- a/src/views/linear_layout.rs +++ b/src/views/linear_layout.rs @@ -327,11 +327,10 @@ impl View for LinearLayout { // eprintln!("Printer size: {:?}", printer.size); // eprintln!("Child size: {:?}", item.child.size); // eprintln!("Offset: {:?}", item.offset); - let printer = &printer.sub_printer( - self.orientation.make_vec(item.offset, 0), - item.child.size, - i == self.focus, - ); + let printer = &printer + .offset(self.orientation.make_vec(item.offset, 0)) + .cropped(item.child.size) + .focused(i == self.focus); item.child.view.draw(printer); } } diff --git a/src/views/list_view.rs b/src/views/list_view.rs index b7785f8..749b370 100644 --- a/src/views/list_view.rs +++ b/src/views/list_view.rs @@ -279,7 +279,9 @@ impl View for ListView { .draw(printer, |printer, i| match self.children[i] { ListChild::Row(ref label, ref view) => { printer.print((0, 0), label); - view.draw(&printer.offset((offset, 0), i == self.focus)); + view.draw(&printer + .offset((offset, 0)) + .focused(i == self.focus)); } ListChild::Delimiter => (), }); diff --git a/src/views/menu_popup.rs b/src/views/menu_popup.rs index 4d871da..1c006bd 100644 --- a/src/views/menu_popup.rs +++ b/src/views/menu_popup.rs @@ -211,7 +211,7 @@ impl View for MenuPopup { let h = self.menu.len(); // If we're too high, add a vertical offset let offset = self.align.v.get_offset(h, printer.size.y); - let printer = &printer.offset((0, offset), true); + let printer = &printer.offset((0, offset)); // Start with a box printer.print_box(Vec2::new(0, 0), printer.size, false); @@ -219,8 +219,7 @@ impl View for MenuPopup { // We're giving it a reduced size because of borders. // But we're keeping the full width, // to integrate horizontal delimiters in the frame. - let size = printer.size - (0, 2); - let printer = printer.sub_printer((0, 1), size, true); + let printer = printer.offset((0, 1)).shrinked((0, 1)); self.scrollbase.draw(&printer, |printer, i| { printer.with_selection(i == self.focus, |printer| { diff --git a/src/views/panel.rs b/src/views/panel.rs index 238823c..2aa6ab3 100644 --- a/src/views/panel.rs +++ b/src/views/panel.rs @@ -35,8 +35,7 @@ impl ViewWrapper for Panel { fn wrap_draw(&self, printer: &Printer) { printer.print_box((0, 0), printer.size, true); - let size = printer.size.saturating_sub((2, 2)); - let printer = printer.sub_printer((1, 1), size, true); + let printer = printer.offset((1, 1)).shrinked((1, 1)); self.view.draw(&printer); } diff --git a/src/views/progress_bar.rs b/src/views/progress_bar.rs index 44538e4..781a652 100644 --- a/src/views/progress_bar.rs +++ b/src/views/progress_bar.rs @@ -215,7 +215,7 @@ impl View for ProgressBar { printer.with_effect(Effect::Reverse, |printer| { printer.print((offset, 0), &label); }); - let printer = &printer.sub_printer((0, 0), (length, 1), true); + let printer = &printer.cropped((length, 1)); printer.print_hline((0, 0), length, " "); printer.print((offset, 0), &label); }); diff --git a/src/views/scroll_view.rs b/src/views/scroll_view.rs index 40ea325..88b5ecd 100644 --- a/src/views/scroll_view.rs +++ b/src/views/scroll_view.rs @@ -6,12 +6,17 @@ use vec::Vec2; pub struct ScrollView { inner: V, offset: Vec2, + + // Togglable horizontal/vertical scrolling? } impl View for ScrollView where V: View { fn draw(&self, printer: &Printer) { - self.printer.offset - self.inner.draw(printer); + // Draw content + let printer = printer.content_offset(self.offset); + self.inner.draw(&printer); + + // Draw scrollbar? } } diff --git a/src/views/select_view.rs b/src/views/select_view.rs index 984ff23..92bb86d 100644 --- a/src/views/select_view.rs +++ b/src/views/select_view.rs @@ -703,8 +703,7 @@ impl View for SelectView { } else { let h = self.items.len(); let offset = self.align.v.get_offset(h, printer.size.y); - let printer = - &printer.sub_printer(Vec2::new(0, offset), printer.size, true); + let printer = &printer.offset((0, offset)); self.scrollbase.draw(printer, |printer, i| { printer.with_selection(i == self.focus(), |printer| { diff --git a/src/views/shadow_view.rs b/src/views/shadow_view.rs index 2813ab1..911b3fc 100644 --- a/src/views/shadow_view.rs +++ b/src/views/shadow_view.rs @@ -86,7 +86,7 @@ impl ViewWrapper for ShadowView { self.left_padding as usize, self.top_padding as usize, ); - let printer = &printer.offset(offset, true); + let printer = &printer.offset(offset); if printer.theme.shadow { let h = printer.size.y; let w = printer.size.x; @@ -102,11 +102,7 @@ impl ViewWrapper for ShadowView { } // Draw the view background - let printer = printer.sub_printer( - Vec2::zero(), - printer.size.saturating_sub((1, 1)), - true, - ); + let printer = printer.shrinked((1,1)); self.view.draw(&printer); } } diff --git a/src/views/stack_view.rs b/src/views/stack_view.rs index a13c459..58b96ea 100644 --- a/src/views/stack_view.rs +++ b/src/views/stack_view.rs @@ -420,11 +420,10 @@ impl StackView { StackPositionIterator::new(self.layers.iter(), printer.size) .enumerate() { - v.view.draw(&printer.sub_printer( - offset, - v.size, - i + 1 == last, - )); + v.view.draw(&printer + .offset(offset) + .cropped(v.size) + .focused(i + 1 == last)); } }); } diff --git a/src/views/text_view.rs b/src/views/text_view.rs index 16ff859..7819442 100644 --- a/src/views/text_view.rs +++ b/src/views/text_view.rs @@ -455,7 +455,7 @@ impl View for TextView { let h = self.rows.len(); // If the content is smaller than the view, align it somewhere. let offset = self.align.v.get_offset(h, printer.size.y); - let printer = &printer.offset((0, offset), true); + let printer = &printer.offset((0, offset)); let content = self.content.lock().unwrap();