From 32ee2de697577135df0e554b47e59740fc0010d0 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Tue, 1 May 2018 17:55:13 -0700 Subject: [PATCH] Scrollview: take scrollbar size into account --- src/views/scroll_view.rs | 62 ++++++++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/src/views/scroll_view.rs b/src/views/scroll_view.rs index 451c88b..21010ac 100644 --- a/src/views/scroll_view.rs +++ b/src/views/scroll_view.rs @@ -4,8 +4,8 @@ use rect::Rect; use vec::Vec2; use view::{Selector, View}; use xy::XY; -use With; use Printer; +use With; /// Wraps a view in a scrollable area. pub struct ScrollView { @@ -20,6 +20,12 @@ pub struct ScrollView { // Can we scroll horizontally? enabled: XY, + + // Should we show scrollbars? + show_scrollbars: bool, + + // How much padding should be between content and scrollbar? + scrollbar_padding: Vec2, } impl ScrollView { @@ -31,6 +37,8 @@ impl ScrollView { offset: Vec2::zero(), last_size: Vec2::zero(), enabled: XY::new(false, true), + show_scrollbars: true, + scrollbar_padding: Vec2::new(1, 0), } } @@ -81,13 +89,34 @@ impl ScrollView { } } +impl ScrollView where V: View { + + fn compute_size(&mut self, constraint: Vec2, scrollable: XY) -> (Vec2, XY) { + let scrollbar_size = scrollable + .select_or(self.scrollbar_padding + (1, 1), Vec2::zero()); + + let inner_size = self.inner + .required_size(constraint.saturating_sub(scrollbar_size)); + + let size = self.enabled + .select_or(Vec2::min(inner_size, constraint), inner_size) + + scrollbar_size; + + let new_scrollable = inner_size.zip_map(size, |i, s| i > s); + + (size, new_scrollable) + } +} + impl View for ScrollView where V: View, { fn draw(&self, printer: &Printer) { // Draw content - let printer = printer.content_offset(self.offset).inner_size(self.inner_size); + let printer = printer + .content_offset(self.offset) + .inner_size(self.inner_size); self.inner.draw(&printer); // Draw scrollbar? @@ -154,13 +183,30 @@ where } fn required_size(&mut self, constraint: Vec2) -> Vec2 { - // TODO: depend on allowed axis - let size = self.inner.required_size(constraint); + // Attempt 1: try without scrollbars + let (size, scrollable) = self.compute_size(constraint, XY::new(false, false)); - // For each enabled direction, return the min of size and constraint. - // For the rest, directly return the size. - self.enabled - .select_or(Vec2::min(size, constraint), size) + // Did it work? + if scrollable.any() && self.show_scrollbars { + // Attempt 2: he wants to scroll? Sure! Try again with some space for the scrollbar. + let (size, new_scrollable) = self.compute_size(constraint, scrollable); + if scrollable != new_scrollable { + // Again? We're now scrolling in a new direction? + // There is no end to this! + let (size, _) = self.compute_size(constraint, new_scrollable); + + // That's enough. If the inner view changed again, ignore it! + // That'll teach it. + size + } else { + // Yup, scrolling did it. We're goot to go now. + size + } + } else { + // We're not showing any scrollbar, either because we don't scroll + // or because scrollbars are hidden. + size + } } fn call_on_any<'a>(&mut self, selector: &Selector, cb: AnyCb<'a>) {