Initial scrollview implementation

This commit is contained in:
Alexandre Bury 2018-04-16 22:39:46 -07:00
parent 56ce95f9b6
commit 1f6de5a591

View File

@ -1,17 +1,58 @@
use view::View;
use Printer; use Printer;
use direction::Direction;
use event::{AnyCb, Event, EventResult, Key};
use rect::Rect;
use std::cmp::min;
use vec::Vec2; use vec::Vec2;
use view::{Selector, View};
use xy::XY;
/// Wraps a view in a scrollable area. /// Wraps a view in a scrollable area.
pub struct ScrollView<V> { pub struct ScrollView<V> {
inner_size: Vec2,
inner: V, inner: V,
// Offset into the inner view.
//
// Our `(0,0)` will be inner's `offset`
offset: Vec2, offset: Vec2,
// Togglable horizontal/vertical scrolling? last_size: Vec2,
// Can we scroll horizontally?
enabled: XY<bool>,
} }
impl <V> View for ScrollView<V> where V: View { impl<V> ScrollView<V> {
/// 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<S>(&mut self, offset: S)
where
S: Into<Vec2>,
{
let max_offset = self.inner_size.saturating_sub(self.last_size);
self.offset = offset.into().or_min(max_offset);
}
}
impl<V> View for ScrollView<V>
where
V: View,
{
fn draw(&self, printer: &Printer) { fn draw(&self, printer: &Printer) {
// Draw content // Draw content
let printer = printer.content_offset(self.offset); let printer = printer.content_offset(self.offset);
@ -19,4 +60,67 @@ impl <V> View for ScrollView<V> where V: View {
// Draw scrollbar? // 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
}
} }