mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 09:25:01 +00:00
Add ScrollView::on_scroll
This commit is contained in:
parent
16b4908443
commit
9bc1cd04c3
@ -68,6 +68,17 @@ pub fn immutify<F: FnMut(&mut Cursive)>(
|
|||||||
/// ```
|
/// ```
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! immut1 {
|
macro_rules! immut1 {
|
||||||
|
($f:expr ; else $else:expr) => {{
|
||||||
|
let callback = ::std::cell::RefCell::new($f);
|
||||||
|
move |s| {
|
||||||
|
if let ::std::result::Result::Ok(mut f) = callback.try_borrow_mut()
|
||||||
|
{
|
||||||
|
(&mut *f)(s)
|
||||||
|
} else {
|
||||||
|
$else
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}};
|
||||||
($f:expr) => {{
|
($f:expr) => {{
|
||||||
let callback = ::std::cell::RefCell::new($f);
|
let callback = ::std::cell::RefCell::new($f);
|
||||||
move |s| {
|
move |s| {
|
||||||
@ -120,11 +131,22 @@ macro_rules! once1 {
|
|||||||
/// assign it to a variable.
|
/// assign it to a variable.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! immut2 {
|
macro_rules! immut2 {
|
||||||
|
($f:expr ; else $else:expr) => {{
|
||||||
|
let callback = ::std::cell::RefCell::new($f);
|
||||||
|
move |s, t| {
|
||||||
|
if let ::std::result::Result::Ok(mut f) = callback.try_borrow_mut()
|
||||||
|
{
|
||||||
|
(&mut *f)(s, t)
|
||||||
|
} else {
|
||||||
|
$else
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}};
|
||||||
($f:expr) => {{
|
($f:expr) => {{
|
||||||
let callback = ::std::cell::RefCell::new($f);
|
let callback = ::std::cell::RefCell::new($f);
|
||||||
move |s, t| {
|
move |s, t| {
|
||||||
if let Ok(mut f) = callback.try_borrow_mut() {
|
if let Ok(mut f) = callback.try_borrow_mut() {
|
||||||
(&mut *f)(s, t)
|
(&mut *f)(s, t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
@ -147,11 +169,22 @@ macro_rules! immut2 {
|
|||||||
/// assign it to a variable.
|
/// assign it to a variable.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! immut3 {
|
macro_rules! immut3 {
|
||||||
|
($f:expr ; else $else:expr) => {{
|
||||||
|
let callback = ::std::cell::RefCell::new($f);
|
||||||
|
move |s, t, t| {
|
||||||
|
if let ::std::result::Result::Ok(mut f) = callback.try_borrow_mut()
|
||||||
|
{
|
||||||
|
(&mut *f)(s, t, u)
|
||||||
|
} else {
|
||||||
|
$else
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}};
|
||||||
($f:expr) => {{
|
($f:expr) => {{
|
||||||
let callback = ::std::cell::RefCell::new($f);
|
let callback = ::std::cell::RefCell::new($f);
|
||||||
move |s, t, u| {
|
move |s, t, u| {
|
||||||
if let Ok(mut f) = callback.try_borrow_mut() {
|
if let Ok(mut f) = callback.try_borrow_mut() {
|
||||||
(&mut *f)(s, t, u)
|
(&mut *f)(s, t, u);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
use crate::direction::Direction;
|
use crate::{
|
||||||
use crate::event::{AnyCb, Event, EventResult};
|
direction::Direction,
|
||||||
use crate::view::{scroll, ScrollStrategy, Selector, View};
|
event::{AnyCb, Event, EventResult},
|
||||||
use crate::{Printer, Rect, Vec2, With};
|
view::{scroll, ScrollStrategy, Selector, View},
|
||||||
|
Cursive, Printer, Rect, Vec2, With,
|
||||||
|
};
|
||||||
|
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
/// Wraps a view in a scrollable area.
|
/// Wraps a view in a scrollable area.
|
||||||
pub struct ScrollView<V> {
|
pub struct ScrollView<V> {
|
||||||
@ -9,6 +13,8 @@ pub struct ScrollView<V> {
|
|||||||
inner: V,
|
inner: V,
|
||||||
|
|
||||||
core: scroll::Core,
|
core: scroll::Core,
|
||||||
|
|
||||||
|
on_scroll: Rc<dyn Fn(&mut Self, Rect) -> EventResult>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_scroller!(ScrollView<V>::core);
|
impl_scroller!(ScrollView<V>::core);
|
||||||
@ -22,6 +28,7 @@ where
|
|||||||
ScrollView {
|
ScrollView {
|
||||||
inner,
|
inner,
|
||||||
core: scroll::Core::new(),
|
core: scroll::Core::new(),
|
||||||
|
on_scroll: Rc::new(|_, _| EventResult::Ignored),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,15 +77,23 @@ where
|
|||||||
///
|
///
|
||||||
/// It is reset to `ScrollStrategy::KeepRow` whenever the user scrolls
|
/// It is reset to `ScrollStrategy::KeepRow` whenever the user scrolls
|
||||||
/// manually.
|
/// manually.
|
||||||
pub fn set_scroll_strategy(&mut self, strategy: ScrollStrategy) {
|
pub fn set_scroll_strategy(
|
||||||
|
&mut self,
|
||||||
|
strategy: ScrollStrategy,
|
||||||
|
) -> EventResult {
|
||||||
self.core.set_scroll_strategy(strategy);
|
self.core.set_scroll_strategy(strategy);
|
||||||
|
|
||||||
|
// Scrolling may have happened.
|
||||||
|
self.on_scroll_callback()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Defines the way scrolling is adjusted on content or size change.
|
/// Defines the way scrolling is adjusted on content or size change.
|
||||||
///
|
///
|
||||||
/// Chainable variant.
|
/// Chainable variant.
|
||||||
pub fn scroll_strategy(self, strategy: ScrollStrategy) -> Self {
|
pub fn scroll_strategy(self, strategy: ScrollStrategy) -> Self {
|
||||||
self.with(|s| s.set_scroll_strategy(strategy))
|
self.with(|s| {
|
||||||
|
s.set_scroll_strategy(strategy);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Control whether scroll bars are visibile.
|
/// Control whether scroll bars are visibile.
|
||||||
@ -96,25 +111,31 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the scroll offset to the given value
|
/// Sets the scroll offset to the given value
|
||||||
pub fn set_offset<S>(&mut self, offset: S)
|
pub fn set_offset<S>(&mut self, offset: S) -> EventResult
|
||||||
where
|
where
|
||||||
S: Into<Vec2>,
|
S: Into<Vec2>,
|
||||||
{
|
{
|
||||||
self.core.set_offset(offset);
|
self.core.set_offset(offset);
|
||||||
|
|
||||||
|
self.on_scroll_callback()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Controls whether this view can scroll vertically.
|
/// Controls whether this view can scroll vertically.
|
||||||
///
|
///
|
||||||
/// Defaults to `true`.
|
/// Defaults to `true`.
|
||||||
pub fn set_scroll_y(&mut self, enabled: bool) {
|
pub fn set_scroll_y(&mut self, enabled: bool) -> EventResult {
|
||||||
self.core.set_scroll_y(enabled);
|
self.core.set_scroll_y(enabled);
|
||||||
|
|
||||||
|
self.on_scroll_callback()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Controls whether this view can scroll horizontally.
|
/// Controls whether this view can scroll horizontally.
|
||||||
///
|
///
|
||||||
/// Defaults to `false`.
|
/// Defaults to `false`.
|
||||||
pub fn set_scroll_x(&mut self, enabled: bool) {
|
pub fn set_scroll_x(&mut self, enabled: bool) -> EventResult {
|
||||||
self.core.set_scroll_x(enabled);
|
self.core.set_scroll_x(enabled);
|
||||||
|
|
||||||
|
self.on_scroll_callback()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Controls whether this view can scroll vertically.
|
/// Controls whether this view can scroll vertically.
|
||||||
@ -123,7 +144,9 @@ where
|
|||||||
///
|
///
|
||||||
/// Chainable variant.
|
/// Chainable variant.
|
||||||
pub fn scroll_y(self, enabled: bool) -> Self {
|
pub fn scroll_y(self, enabled: bool) -> Self {
|
||||||
self.with(|s| s.set_scroll_y(enabled))
|
self.with(|s| {
|
||||||
|
s.set_scroll_y(enabled);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Controls whether this view can scroll horizontally.
|
/// Controls whether this view can scroll horizontally.
|
||||||
@ -132,33 +155,45 @@ where
|
|||||||
///
|
///
|
||||||
/// Chainable variant.
|
/// Chainable variant.
|
||||||
pub fn scroll_x(self, enabled: bool) -> Self {
|
pub fn scroll_x(self, enabled: bool) -> Self {
|
||||||
self.with(|s| s.set_scroll_x(enabled))
|
self.with(|s| {
|
||||||
|
s.set_scroll_x(enabled);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Programmatically scroll to the top of the view.
|
/// Programmatically scroll to the top of the view.
|
||||||
pub fn scroll_to_top(&mut self) {
|
pub fn scroll_to_top(&mut self) -> EventResult {
|
||||||
self.core.scroll_to_top();
|
self.core.scroll_to_top();
|
||||||
|
|
||||||
|
self.on_scroll_callback()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Programmatically scroll to the bottom of the view.
|
/// Programmatically scroll to the bottom of the view.
|
||||||
pub fn scroll_to_bottom(&mut self) {
|
pub fn scroll_to_bottom(&mut self) -> EventResult {
|
||||||
self.core.scroll_to_bottom();
|
self.core.scroll_to_bottom();
|
||||||
|
|
||||||
|
self.on_scroll_callback()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Programmatically scroll to the leftmost side of the view.
|
/// Programmatically scroll to the leftmost side of the view.
|
||||||
pub fn scroll_to_left(&mut self) {
|
pub fn scroll_to_left(&mut self) -> EventResult {
|
||||||
self.core.scroll_to_left();
|
self.core.scroll_to_left();
|
||||||
|
|
||||||
|
self.on_scroll_callback()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Programmatically scroll to the rightmost side of the view.
|
/// Programmatically scroll to the rightmost side of the view.
|
||||||
pub fn scroll_to_right(&mut self) {
|
pub fn scroll_to_right(&mut self) -> EventResult {
|
||||||
self.core.scroll_to_right();
|
self.core.scroll_to_right();
|
||||||
|
|
||||||
|
self.on_scroll_callback()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Programmatically scroll until the child's important area is in view.
|
/// Programmatically scroll until the child's important area is in view.
|
||||||
pub fn scroll_to_important_area(&mut self) {
|
pub fn scroll_to_important_area(&mut self) -> EventResult {
|
||||||
let important_area = self.inner.important_area(self.core.last_size());
|
let important_area = self.inner.important_area(self.core.last_size());
|
||||||
self.core.scroll_to_rect(important_area);
|
self.core.scroll_to_rect(important_area);
|
||||||
|
|
||||||
|
self.on_scroll_callback()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the wrapped view.
|
/// Returns the wrapped view.
|
||||||
@ -166,6 +201,108 @@ where
|
|||||||
self.inner
|
self.inner
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets a callback to be run whenever scrolling happens.
|
||||||
|
///
|
||||||
|
/// This lets the callback access the `ScrollView` itself (and its child)
|
||||||
|
/// if necessary.
|
||||||
|
///
|
||||||
|
/// If you just need to run a callback on `&mut Cursive`, consider
|
||||||
|
/// `set_on_scroll`.
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets a callback to be run whenever scrolling happens.
|
||||||
|
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))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wrap a function and only calls it if the second parameter changed.
|
||||||
|
///
|
||||||
|
/// Not 100% generic, only works for our use-case here.
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets a callback to be run whenever the scroll offset changes.
|
||||||
|
pub fn set_on_scroll_change_inner<F>(&mut self, on_scroll: F)
|
||||||
|
where
|
||||||
|
F: FnMut(&mut Self, Rect) -> EventResult + 'static,
|
||||||
|
{
|
||||||
|
self.set_on_scroll_inner(Self::skip_unchanged(on_scroll, || {
|
||||||
|
EventResult::Ignored
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets a callback to be run whenever the scroll offset changes.
|
||||||
|
pub fn set_on_scroll_change<F>(&mut self, on_scroll: F)
|
||||||
|
where
|
||||||
|
F: FnMut(&mut Cursive, Rect) + 'static,
|
||||||
|
{
|
||||||
|
self.set_on_scroll(Self::skip_unchanged(on_scroll, || ()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets a callback to be run whenever scrolling happens.
|
||||||
|
///
|
||||||
|
/// This lets the callback access the `ScrollView` itself (and its child)
|
||||||
|
/// if necessary.
|
||||||
|
///
|
||||||
|
/// If you just need to run a callback on `&mut Cursive`, consider
|
||||||
|
/// `set_on_scroll`.
|
||||||
|
///
|
||||||
|
/// Chainable variant.
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets a callback to be run whenever scrolling happens.
|
||||||
|
///
|
||||||
|
/// Chainable variant.
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run any callback after scrolling.
|
||||||
|
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);
|
inner_getters!(self.inner: V);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,12 +315,16 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(&mut self, event: Event) -> EventResult {
|
fn on_event(&mut self, event: Event) -> EventResult {
|
||||||
scroll::on_event(
|
match scroll::on_event(
|
||||||
self,
|
self,
|
||||||
event,
|
event,
|
||||||
|s, e| s.inner.on_event(e),
|
|s, e| s.inner.on_event(e),
|
||||||
|s, si| s.inner.important_area(si),
|
|s, si| s.inner.important_area(si),
|
||||||
)
|
) {
|
||||||
|
EventResult::Ignored => EventResult::Ignored,
|
||||||
|
// If the event was consumed, then we may have scrolled.
|
||||||
|
other => other.and(self.on_scroll_callback()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(&mut self, size: Vec2) {
|
fn layout(&mut self, size: Vec2) {
|
||||||
@ -225,6 +366,9 @@ where
|
|||||||
// If the inner view takes focus, re-align the important area.
|
// If the inner view takes focus, re-align the important area.
|
||||||
if self.inner.take_focus(source) {
|
if self.inner.take_focus(source) {
|
||||||
self.scroll_to_important_area();
|
self.scroll_to_important_area();
|
||||||
|
|
||||||
|
// Note: we can't really return an `EventResult` here :(
|
||||||
|
self.on_scroll_callback();
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
self.core.is_scrolling().any()
|
self.core.is_scrolling().any()
|
||||||
|
Loading…
Reference in New Issue
Block a user