From 1f6de5a591af3fed2f159874e0eae6febbdb5941 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Mon, 16 Apr 2018 22:39:46 -0700 Subject: [PATCH] Initial scrollview implementation --- src/views/scroll_view.rs | 110 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 107 insertions(+), 3 deletions(-) diff --git a/src/views/scroll_view.rs b/src/views/scroll_view.rs index 88b5ecd..252c089 100644 --- a/src/views/scroll_view.rs +++ b/src/views/scroll_view.rs @@ -1,17 +1,58 @@ -use view::View; use Printer; +use direction::Direction; +use event::{AnyCb, Event, EventResult, Key}; +use rect::Rect; +use std::cmp::min; use vec::Vec2; +use view::{Selector, View}; +use xy::XY; /// Wraps a view in a scrollable area. pub struct ScrollView { + inner_size: Vec2, inner: V, + // Offset into the inner view. + // + // Our `(0,0)` will be inner's `offset` offset: Vec2, - // Togglable horizontal/vertical scrolling? + last_size: Vec2, + + // Can we scroll horizontally? + enabled: XY, } -impl View for ScrollView where V: View { +impl ScrollView { + /// Creates a new ScrollView around `view`. + pub fn new(view: V) -> Self { + ScrollView { + inner: view, + inner_size: Vec2::zero(), + offset: Vec2::zero(), + last_size: Vec2::zero(), + enabled: XY::new(false, true), + } + } + /// Returns the viewport in the inner content. + pub fn content_viewport(&self) -> Rect { + Rect::from_size(self.offset, self.last_size) + } + + /// Sets the scroll offset to the given value + pub fn set_offset(&mut self, offset: S) + where + S: Into, + { + let max_offset = self.inner_size.saturating_sub(self.last_size); + self.offset = offset.into().or_min(max_offset); + } +} + +impl View for ScrollView +where + V: View, +{ fn draw(&self, printer: &Printer) { // Draw content let printer = printer.content_offset(self.offset); @@ -19,4 +60,67 @@ impl View for ScrollView where V: View { // Draw scrollbar? } + + fn on_event(&mut self, event: Event) -> EventResult { + // Relativize event accorging to the offset + let mut relative_event = event.clone(); + if let Some(pos) = relative_event.mouse_position_mut() { + *pos = *pos + self.offset; + } + match self.inner.on_event(relative_event) { + EventResult::Ignored => { + // If it's an arrow, try to scroll in the given direction. + // If it's a mouse scroll, try to scroll as well. + match event { + Event::Key(Key::Up) if self.offset.y > 0 => { + self.offset.y -= 1; + EventResult::Consumed(None) + } + Event::Key(Key::Left) if self.offset.x > 0 => { + self.offset.x -= 1; + EventResult::Consumed(None) + } + _ => EventResult::Ignored, + } + } + other => other, + } + } + + fn layout(&mut self, size: Vec2) { + self.last_size = size; + + // Ask one more time + self.inner_size = self.inner.required_size(size).or_max(size); + self.inner.layout(self.inner_size); + + // TODO: Refresh offset if needed! + } + + fn needs_relayout(&self) -> bool { + self.inner.needs_relayout() + } + + fn required_size(&mut self, constraint: Vec2) -> Vec2 { + // TODO: depend on allowed axis + let size = self.inner.required_size(constraint); + + // 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) + } + + fn call_on_any<'a>(&mut self, selector: &Selector, cb: AnyCb<'a>) { + self.inner.call_on_any(selector, cb) + } + + fn focus_view(&mut self, selector: &Selector) -> Result<(), ()> { + self.inner.focus_view(selector) + } + + fn take_focus(&mut self, source: Direction) -> bool { + let is_scrollable = + self.enabled.any() && (self.inner_size != self.last_size); + self.inner.take_focus(source) || is_scrollable + } }