mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-24 01:46:31 +00:00
Add scroll strategy to ScrollView
This commit is contained in:
parent
9bd1eb320d
commit
2935f0f569
@ -4,7 +4,7 @@ use direction::{Direction, Orientation};
|
|||||||
use event::{AnyCb, Event, EventResult, Key, MouseButton, MouseEvent};
|
use event::{AnyCb, Event, EventResult, Key, MouseButton, MouseEvent};
|
||||||
use rect::Rect;
|
use rect::Rect;
|
||||||
use theme::ColorStyle;
|
use theme::ColorStyle;
|
||||||
use view::{Selector, SizeCache, View};
|
use view::{ScrollStrategy, Selector, SizeCache, View};
|
||||||
use {Printer, Vec2, With, XY};
|
use {Printer, Vec2, With, XY};
|
||||||
|
|
||||||
/// Wraps a view in a scrollable area.
|
/// Wraps a view in a scrollable area.
|
||||||
@ -45,9 +45,15 @@ pub struct ScrollView<V> {
|
|||||||
|
|
||||||
/// We keep the cache here so it can be busted when we change the content.
|
/// We keep the cache here so it can be busted when we change the content.
|
||||||
size_cache: Option<XY<SizeCache>>,
|
size_cache: Option<XY<SizeCache>>,
|
||||||
|
|
||||||
|
/// Defines how to update the offset when the view size changes.
|
||||||
|
scroll_strategy: ScrollStrategy,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V> ScrollView<V> {
|
impl<V> ScrollView<V>
|
||||||
|
where
|
||||||
|
V: View,
|
||||||
|
{
|
||||||
/// Creates a new ScrollView around `view`.
|
/// Creates a new ScrollView around `view`.
|
||||||
pub fn new(inner: V) -> Self {
|
pub fn new(inner: V) -> Self {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
@ -60,6 +66,7 @@ impl<V> ScrollView<V> {
|
|||||||
scrollbar_padding: Vec2::new(1, 0),
|
scrollbar_padding: Vec2::new(1, 0),
|
||||||
thumb_grab: None,
|
thumb_grab: None,
|
||||||
size_cache: None,
|
size_cache: None,
|
||||||
|
scroll_strategy: ScrollStrategy::KeepRow,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,6 +75,25 @@ impl<V> ScrollView<V> {
|
|||||||
Rect::from_size(self.offset, self.available_size())
|
Rect::from_size(self.offset, self.available_size())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Defines the way scrolling is adjusted on content or size change.
|
||||||
|
///
|
||||||
|
/// The scroll strategy defines how the scrolling position is adjusted
|
||||||
|
/// when the size of the view or the content change.
|
||||||
|
///
|
||||||
|
/// It is reset to `ScrollStrategy::KeepRow` whenever the user scrolls
|
||||||
|
/// manually.
|
||||||
|
pub fn set_scroll_strategy(&mut self, strategy: ScrollStrategy) {
|
||||||
|
self.scroll_strategy = strategy;
|
||||||
|
self.adjust_scroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Defines the way scrolling is adjusted on content or size change.
|
||||||
|
///
|
||||||
|
/// Chainable variant.
|
||||||
|
pub fn scroll_strategy(self, strategy: ScrollStrategy) -> Self {
|
||||||
|
self.with(|s| s.set_scroll_strategy(strategy))
|
||||||
|
}
|
||||||
|
|
||||||
/// 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)
|
||||||
where
|
where
|
||||||
@ -165,12 +191,7 @@ impl<V> ScrollView<V> {
|
|||||||
fn available_size(&self) -> Vec2 {
|
fn available_size(&self) -> Vec2 {
|
||||||
self.last_size.saturating_sub(self.scrollbar_size())
|
self.last_size.saturating_sub(self.scrollbar_size())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<V> ScrollView<V>
|
|
||||||
where
|
|
||||||
V: View,
|
|
||||||
{
|
|
||||||
/// Compute the size we would need.
|
/// Compute the size we would need.
|
||||||
///
|
///
|
||||||
/// Given the constraints, and the axis that need scrollbars.
|
/// Given the constraints, and the axis that need scrollbars.
|
||||||
@ -330,6 +351,15 @@ where
|
|||||||
self.inner_size = inner_size;
|
self.inner_size = inner_size;
|
||||||
self.size_cache = Some(SizeCache::build(inner_size, constraint));
|
self.size_cache = Some(SizeCache::build(inner_size, constraint));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Apply the scrolling strategy to the current scroll position.
|
||||||
|
fn adjust_scroll(&mut self) {
|
||||||
|
match self.scroll_strategy {
|
||||||
|
ScrollStrategy::StickToTop => self.scroll_to_top(),
|
||||||
|
ScrollStrategy::StickToBottom => self.scroll_to_bottom(),
|
||||||
|
ScrollStrategy::KeepRow => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V> View for ScrollView<V>
|
impl<V> View for ScrollView<V>
|
||||||
@ -417,7 +447,6 @@ where
|
|||||||
} if self.enabled.y && self.offset.y > 0 =>
|
} if self.enabled.y && self.offset.y > 0 =>
|
||||||
{
|
{
|
||||||
self.offset.y = self.offset.y.saturating_sub(3);
|
self.offset.y = self.offset.y.saturating_sub(3);
|
||||||
EventResult::Consumed(None)
|
|
||||||
}
|
}
|
||||||
Event::Mouse {
|
Event::Mouse {
|
||||||
event: MouseEvent::WheelDown,
|
event: MouseEvent::WheelDown,
|
||||||
@ -432,7 +461,6 @@ where
|
|||||||
.saturating_sub(self.available_size().y),
|
.saturating_sub(self.available_size().y),
|
||||||
self.offset.y + 3,
|
self.offset.y + 3,
|
||||||
);
|
);
|
||||||
EventResult::Consumed(None)
|
|
||||||
}
|
}
|
||||||
Event::Mouse {
|
Event::Mouse {
|
||||||
event: MouseEvent::Press(MouseButton::Left),
|
event: MouseEvent::Press(MouseButton::Left),
|
||||||
@ -443,7 +471,7 @@ where
|
|||||||
.map(|position| self.start_drag(position))
|
.map(|position| self.start_drag(position))
|
||||||
.unwrap_or(false) =>
|
.unwrap_or(false) =>
|
||||||
{
|
{
|
||||||
EventResult::Consumed(None)
|
// Just consume the event.
|
||||||
}
|
}
|
||||||
Event::Mouse {
|
Event::Mouse {
|
||||||
event: MouseEvent::Hold(MouseButton::Left),
|
event: MouseEvent::Hold(MouseButton::Left),
|
||||||
@ -452,19 +480,16 @@ where
|
|||||||
} => {
|
} => {
|
||||||
let position = position.saturating_sub(offset);
|
let position = position.saturating_sub(offset);
|
||||||
self.drag(position);
|
self.drag(position);
|
||||||
EventResult::Consumed(None)
|
|
||||||
}
|
}
|
||||||
Event::Mouse {
|
Event::Mouse {
|
||||||
event: MouseEvent::Release(MouseButton::Left),
|
event: MouseEvent::Release(MouseButton::Left),
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
self.release_grab();
|
self.release_grab();
|
||||||
EventResult::Consumed(None)
|
|
||||||
}
|
}
|
||||||
Event::Key(Key::Home) if self.enabled.any() => {
|
Event::Key(Key::Home) if self.enabled.any() => {
|
||||||
self.offset =
|
self.offset =
|
||||||
self.enabled.select_or(Vec2::zero(), self.offset);
|
self.enabled.select_or(Vec2::zero(), self.offset);
|
||||||
EventResult::Consumed(None)
|
|
||||||
}
|
}
|
||||||
Event::Key(Key::End) if self.enabled.any() => {
|
Event::Key(Key::End) if self.enabled.any() => {
|
||||||
let max_offset = self
|
let max_offset = self
|
||||||
@ -472,19 +497,16 @@ where
|
|||||||
.saturating_sub(self.available_size());
|
.saturating_sub(self.available_size());
|
||||||
self.offset =
|
self.offset =
|
||||||
self.enabled.select_or(max_offset, self.offset);
|
self.enabled.select_or(max_offset, self.offset);
|
||||||
EventResult::Consumed(None)
|
|
||||||
}
|
}
|
||||||
Event::Ctrl(Key::Up) | Event::Key(Key::Up)
|
Event::Ctrl(Key::Up) | Event::Key(Key::Up)
|
||||||
if self.enabled.y && self.offset.y > 0 =>
|
if self.enabled.y && self.offset.y > 0 =>
|
||||||
{
|
{
|
||||||
self.offset.y -= 1;
|
self.offset.y -= 1;
|
||||||
EventResult::Consumed(None)
|
|
||||||
}
|
}
|
||||||
Event::Key(Key::PageUp)
|
Event::Key(Key::PageUp)
|
||||||
if self.enabled.y && self.offset.y > 0 =>
|
if self.enabled.y && self.offset.y > 0 =>
|
||||||
{
|
{
|
||||||
self.offset.y = self.offset.y.saturating_sub(5);
|
self.offset.y = self.offset.y.saturating_sub(5);
|
||||||
EventResult::Consumed(None)
|
|
||||||
}
|
}
|
||||||
Event::Key(Key::PageDown)
|
Event::Key(Key::PageDown)
|
||||||
if self.enabled.y
|
if self.enabled.y
|
||||||
@ -492,7 +514,6 @@ where
|
|||||||
< self.inner_size.y) =>
|
< self.inner_size.y) =>
|
||||||
{
|
{
|
||||||
self.offset.y += 5;
|
self.offset.y += 5;
|
||||||
EventResult::Consumed(None)
|
|
||||||
}
|
}
|
||||||
Event::Ctrl(Key::Down) | Event::Key(Key::Down)
|
Event::Ctrl(Key::Down) | Event::Key(Key::Down)
|
||||||
if self.enabled.y
|
if self.enabled.y
|
||||||
@ -500,13 +521,11 @@ where
|
|||||||
< self.inner_size.y) =>
|
< self.inner_size.y) =>
|
||||||
{
|
{
|
||||||
self.offset.y += 1;
|
self.offset.y += 1;
|
||||||
EventResult::Consumed(None)
|
|
||||||
}
|
}
|
||||||
Event::Ctrl(Key::Left) | Event::Key(Key::Left)
|
Event::Ctrl(Key::Left) | Event::Key(Key::Left)
|
||||||
if self.enabled.x && self.offset.x > 0 =>
|
if self.enabled.x && self.offset.x > 0 =>
|
||||||
{
|
{
|
||||||
self.offset.x -= 1;
|
self.offset.x -= 1;
|
||||||
EventResult::Consumed(None)
|
|
||||||
}
|
}
|
||||||
Event::Ctrl(Key::Right) | Event::Key(Key::Right)
|
Event::Ctrl(Key::Right) | Event::Key(Key::Right)
|
||||||
if self.enabled.x
|
if self.enabled.x
|
||||||
@ -514,11 +533,15 @@ where
|
|||||||
< self.inner_size.x) =>
|
< self.inner_size.x) =>
|
||||||
{
|
{
|
||||||
self.offset.x += 1;
|
self.offset.x += 1;
|
||||||
|
}
|
||||||
|
_ => return EventResult::Ignored,
|
||||||
|
};
|
||||||
|
|
||||||
|
// We just scrolled manually, so reset the scroll strategy.
|
||||||
|
self.scroll_strategy = ScrollStrategy::KeepRow;
|
||||||
|
// TODO: return callback on_scroll?
|
||||||
EventResult::Consumed(None)
|
EventResult::Consumed(None)
|
||||||
}
|
}
|
||||||
_ => EventResult::Ignored,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
other => {
|
other => {
|
||||||
// Fix offset?
|
// Fix offset?
|
||||||
let important = self.inner.important_area(self.inner_size);
|
let important = self.inner.important_area(self.inner_size);
|
||||||
@ -550,10 +573,13 @@ where
|
|||||||
|
|
||||||
self.inner.layout(self.inner_size);
|
self.inner.layout(self.inner_size);
|
||||||
|
|
||||||
// The offset cannot be more than (content - available)
|
// Keep the offset in the valid range.
|
||||||
self.offset = self
|
self.offset = self
|
||||||
.offset
|
.offset
|
||||||
.or_min(self.inner_size.saturating_sub(self.available_size()));
|
.or_min(self.inner_size.saturating_sub(self.available_size()));
|
||||||
|
|
||||||
|
// Possibly update the offset if we're following a specific strategy.
|
||||||
|
self.adjust_scroll();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn needs_relayout(&self) -> bool {
|
fn needs_relayout(&self) -> bool {
|
||||||
|
@ -381,8 +381,6 @@ impl TextView {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
eprintln!("RECOMPUTE");
|
|
||||||
|
|
||||||
// Completely bust the cache
|
// Completely bust the cache
|
||||||
// Just in case we fail, we don't want to leave a bad cache.
|
// Just in case we fail, we don't want to leave a bad cache.
|
||||||
content.size_cache = None;
|
content.size_cache = None;
|
||||||
|
Loading…
Reference in New Issue
Block a user