use crate::{
direction::Direction,
event::{AnyCb, Event, EventResult},
view::{scroll, ScrollStrategy, Selector, View, ViewNotFound},
Cursive, Printer, Rect, Vec2, With,
};
use std::rc::Rc;
pub struct ScrollView<V> {
inner: V,
core: scroll::Core,
on_scroll: Rc<dyn Fn(&mut Self, Rect) -> EventResult>,
}
new_default!(ScrollView<V: Default>);
impl_scroller!(ScrollView<V>::core);
impl<V> ScrollView<V> {
pub fn new(inner: V) -> Self {
ScrollView {
inner,
core: scroll::Core::new(),
on_scroll: Rc::new(|_, _| EventResult::Ignored),
}
}
pub fn content_viewport(&self) -> Rect {
self.core.content_viewport()
}
pub fn inner_size(&self) -> Vec2 {
self.core.inner_size()
}
pub fn is_at_top(&self) -> bool {
self.content_viewport().top() == 0
}
pub fn is_at_bottom(&self) -> bool {
(1 + self.content_viewport().bottom()) >= self.inner_size().y
}
pub fn is_at_left_edge(&self) -> bool {
self.content_viewport().left() == 0
}
pub fn is_at_right_edge(&self) -> bool {
(1 + self.content_viewport().right()) >= self.inner_size().x
}
pub fn set_scroll_strategy(
&mut self,
strategy: ScrollStrategy,
) -> EventResult {
self.core.set_scroll_strategy(strategy);
self.on_scroll_callback()
}
pub fn scroll_strategy(self, strategy: ScrollStrategy) -> Self {
self.with(|s| {
s.set_scroll_strategy(strategy);
})
}
pub fn set_show_scrollbars(&mut self, show_scrollbars: bool) {
self.core.set_show_scrollbars(show_scrollbars);
}
pub fn show_scrollbars(self, show_scrollbars: bool) -> Self {
self.with(|s| s.set_show_scrollbars(show_scrollbars))
}
pub fn set_offset<S>(&mut self, offset: S) -> EventResult
where
S: Into<Vec2>,
{
self.core.set_offset(offset);
self.on_scroll_callback()
}
pub fn set_scroll_y(&mut self, enabled: bool) -> EventResult {
self.core.set_scroll_y(enabled);
self.on_scroll_callback()
}
pub fn set_scroll_x(&mut self, enabled: bool) -> EventResult {
self.core.set_scroll_x(enabled);
self.on_scroll_callback()
}
pub fn scroll_y(self, enabled: bool) -> Self {
self.with(|s| {
s.set_scroll_y(enabled);
})
}
pub fn scroll_x(self, enabled: bool) -> Self {
self.with(|s| {
s.set_scroll_x(enabled);
})
}
pub fn scroll_to_top(&mut self) -> EventResult {
self.core.scroll_to_top();
self.on_scroll_callback()
}
pub fn scroll_to_bottom(&mut self) -> EventResult {
self.core.scroll_to_bottom();
self.on_scroll_callback()
}
pub fn scroll_to_left(&mut self) -> EventResult {
self.core.scroll_to_left();
self.on_scroll_callback()
}
pub fn scroll_to_right(&mut self) -> EventResult {
self.core.scroll_to_right();
self.on_scroll_callback()
}
pub fn scroll_to_important_area(&mut self) -> EventResult
where
V: View,
{
let important_area =
self.inner.important_area(self.core.last_outer_size());
self.core.scroll_to_rect(important_area);
self.on_scroll_callback()
}
pub fn into_inner(self) -> V {
self.inner
}
pub fn set_on_scroll_inner<F>(&mut self, on_scroll: F)
where
F: FnMut(&mut Self, Rect) -> EventResult + 'static,
{
self.on_scroll =
Rc::new(immut2!(on_scroll; else EventResult::Ignored));
}
pub fn set_on_scroll<F>(&mut self, on_scroll: F)
where
F: FnMut(&mut Cursive, Rect) + 'static,
{
let on_scroll: Rc<dyn Fn(&mut Cursive, Rect)> =
std::rc::Rc::new(immut2!(on_scroll));
self.set_on_scroll_inner(move |_, rect| {
let on_scroll = std::rc::Rc::clone(&on_scroll);
EventResult::with_cb(move |siv| on_scroll(siv, rect))
})
}
fn skip_unchanged<F, T, R, I>(
mut f: F,
mut if_skipped: I,
) -> impl for<'a> FnMut(&'a mut T, Rect) -> R
where
F: for<'a> FnMut(&'a mut T, Rect) -> R + 'static,
I: FnMut() -> R + 'static,
{
let mut previous = Rect::from_size((0, 0), (0, 0));
move |t, r| {
if r != previous {
previous = r;
f(t, r)
} else {
if_skipped()
}
}
}
pub fn set_on_scroll_change_inner<F>(&mut self, on_scroll: F)
where
F: FnMut(&mut Self, Rect) -> EventResult + 'static,
V: 'static,
{
self.set_on_scroll_inner(Self::skip_unchanged(on_scroll, || {
EventResult::Ignored
}));
}
pub fn set_on_scroll_change<F>(&mut self, on_scroll: F)
where
F: FnMut(&mut Cursive, Rect) + 'static,
V: 'static,
{
self.set_on_scroll(Self::skip_unchanged(on_scroll, || ()));
}
pub fn on_scroll_inner<F>(self, on_scroll: F) -> Self
where
F: Fn(&mut Self, Rect) -> EventResult + 'static,
{
self.with(|s| s.set_on_scroll_inner(on_scroll))
}
pub fn on_scroll<F>(self, on_scroll: F) -> Self
where
F: FnMut(&mut crate::Cursive, Rect) + 'static,
{
self.with(|s| s.set_on_scroll(on_scroll))
}
fn on_scroll_callback(&mut self) -> EventResult {
let viewport = self.content_viewport();
let on_scroll = Rc::clone(&self.on_scroll);
(on_scroll)(self, viewport)
}
inner_getters!(self.inner: V);
}
impl<V> View for ScrollView<V>
where
V: View,
{
fn draw(&self, printer: &Printer) {
scroll::draw(self, printer, |s, p| s.inner.draw(p));
}
fn on_event(&mut self, event: Event) -> EventResult {
match scroll::on_event(
self,
event,
|s, e| s.inner.on_event(e),
|s, si| s.inner.important_area(si),
) {
EventResult::Ignored => EventResult::Ignored,
other => other.and(self.on_scroll_callback()),
}
}
fn layout(&mut self, size: Vec2) {
scroll::layout(
self,
size,
self.inner.needs_relayout(),
|s, si| s.inner.layout(si),
|s, c| s.inner.required_size(c),
);
}
fn needs_relayout(&self) -> bool {
self.core.needs_relayout() || self.inner.needs_relayout()
}
fn required_size(&mut self, constraint: Vec2) -> Vec2 {
scroll::required_size(
self,
constraint,
self.inner.needs_relayout(),
|s, c| s.inner.required_size(c),
)
}
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<(), ViewNotFound> {
self.inner.focus_view(selector).map(|()| {
self.scroll_to_important_area();
})
}
fn take_focus(&mut self, source: Direction) -> bool {
if self.inner.take_focus(source) {
if source != Direction::none() {
self.scroll_to_important_area();
self.on_scroll_callback();
}
true
} else {
self.core.is_scrolling().any()
}
}
fn important_area(&self, size: Vec2) -> Rect {
scroll::important_area(self, size, |s, si| s.inner.important_area(si))
}
}