mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-08 18:30:40 +00:00
Add size cache to ScrollView
This commit is contained in:
parent
5be79740ad
commit
9bd1eb320d
@ -24,7 +24,7 @@ pub mod pan;
|
||||
/// threads, ncurses' signal handler is confused and he can't keep track of
|
||||
/// the terminal size. Poor ncurses.
|
||||
fn terminal_size() -> Vec2 {
|
||||
term_size::dimensions().unwrap_or((0,0)).into()
|
||||
term_size::dimensions().unwrap_or((0, 0)).into()
|
||||
}
|
||||
|
||||
fn split_i32(code: i32) -> Vec<u8> {
|
||||
|
@ -126,5 +126,6 @@ pub mod backend;
|
||||
|
||||
pub use cursive::{CbFunc, Cursive, ScreenId};
|
||||
pub use printer::Printer;
|
||||
pub use vec::Vec2;
|
||||
pub use with::With;
|
||||
pub use xy::XY;
|
||||
|
@ -52,9 +52,6 @@ impl SizeCache {
|
||||
/// * `size` must fit inside `req`.
|
||||
/// * for each dimension, `constrained = (size == req)`
|
||||
pub fn build(size: Vec2, req: Vec2) -> XY<Self> {
|
||||
XY::new(
|
||||
SizeCache::new(size.x, size.x >= req.x),
|
||||
SizeCache::new(size.y, size.y >= req.y),
|
||||
)
|
||||
size.zip_map(req, |size, req| SizeCache::new(size, size >= req))
|
||||
}
|
||||
}
|
||||
|
@ -1,57 +1,57 @@
|
||||
use std::cmp::min;
|
||||
|
||||
use direction::{Direction, Orientation};
|
||||
use event::{AnyCb, Event, EventResult, Key, MouseButton, MouseEvent};
|
||||
use rect::Rect;
|
||||
use theme::ColorStyle;
|
||||
use vec::Vec2;
|
||||
use view::{Selector, View};
|
||||
use xy::XY;
|
||||
use Printer;
|
||||
use With;
|
||||
|
||||
use std::cmp::min;
|
||||
use view::{Selector, SizeCache, View};
|
||||
use {Printer, Vec2, With, XY};
|
||||
|
||||
/// Wraps a view in a scrollable area.
|
||||
pub struct ScrollView<V> {
|
||||
// The wrapped view.
|
||||
/// The wrapped view.
|
||||
inner: V,
|
||||
|
||||
// This is the size the child thinks we're giving him.
|
||||
/// This is the size the child thinks we're giving him.
|
||||
inner_size: Vec2,
|
||||
|
||||
// Offset into the inner view.
|
||||
//
|
||||
// Our `(0,0)` will be inner's `offset`
|
||||
/// Offset into the inner view.
|
||||
///
|
||||
/// Our `(0,0)` will be inner's `offset`
|
||||
offset: Vec2,
|
||||
|
||||
// What was our own size last time we checked.
|
||||
//
|
||||
// This includes scrollbars, if any.
|
||||
/// What was our own size last time we checked.
|
||||
///
|
||||
/// This includes scrollbars, if any.
|
||||
last_size: Vec2,
|
||||
|
||||
// Are we scrollable in each direction?
|
||||
/// Are we scrollable in each direction?
|
||||
enabled: XY<bool>,
|
||||
|
||||
// Should we show scrollbars?
|
||||
//
|
||||
// Even if this is true, no scrollbar will be printed if we don't need to
|
||||
// scroll.
|
||||
//
|
||||
// TODO: have an option to always show the scrollbar.
|
||||
// TODO: have an option to show scrollbar on top/left.
|
||||
/// Should we show scrollbars?
|
||||
///
|
||||
/// Even if this is true, no scrollbar will be printed if we don't need to
|
||||
/// scroll.
|
||||
///
|
||||
/// TODO: have an option to always show the scrollbar.
|
||||
/// TODO: have an option to show scrollbar on top/left.
|
||||
show_scrollbars: bool,
|
||||
|
||||
// How much padding should be between content and scrollbar?
|
||||
/// How much padding should be between content and scrollbar?
|
||||
scrollbar_padding: Vec2,
|
||||
|
||||
/// Initial position of the cursor when dragging.
|
||||
thumb_grab: Option<(Orientation, usize)>,
|
||||
|
||||
/// We keep the cache here so it can be busted when we change the content.
|
||||
size_cache: Option<XY<SizeCache>>,
|
||||
}
|
||||
|
||||
impl<V> ScrollView<V> {
|
||||
/// Creates a new ScrollView around `view`.
|
||||
pub fn new(view: V) -> Self {
|
||||
pub fn new(inner: V) -> Self {
|
||||
ScrollView {
|
||||
inner: view,
|
||||
inner,
|
||||
inner_size: Vec2::zero(),
|
||||
offset: Vec2::zero(),
|
||||
last_size: Vec2::zero(),
|
||||
@ -59,6 +59,7 @@ impl<V> ScrollView<V> {
|
||||
show_scrollbars: true,
|
||||
scrollbar_padding: Vec2::new(1, 0),
|
||||
thumb_grab: None,
|
||||
size_cache: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,6 +82,7 @@ impl<V> ScrollView<V> {
|
||||
/// Defaults to `true`.
|
||||
pub fn set_scroll_y(&mut self, enabled: bool) {
|
||||
self.enabled.y = enabled;
|
||||
self.invalidate_cache();
|
||||
}
|
||||
|
||||
/// Controls whether this view can scroll horizontally.
|
||||
@ -88,6 +90,7 @@ impl<V> ScrollView<V> {
|
||||
/// Defaults to `false`.
|
||||
pub fn set_scroll_x(&mut self, enabled: bool) {
|
||||
self.enabled.x = enabled;
|
||||
self.invalidate_cache();
|
||||
}
|
||||
|
||||
/// Controls whether this view can scroll vertically.
|
||||
@ -134,6 +137,11 @@ impl<V> ScrollView<V> {
|
||||
self.set_offset((max_x, curr_y));
|
||||
}
|
||||
|
||||
/// Clears the cache.
|
||||
fn invalidate_cache(&mut self) {
|
||||
self.size_cache = None;
|
||||
}
|
||||
|
||||
/// Returns for each axis if we are scrolling.
|
||||
fn is_scrolling(&self) -> XY<bool> {
|
||||
self.inner_size.zip_map(self.last_size, |i, s| i > s)
|
||||
@ -268,6 +276,7 @@ where
|
||||
///
|
||||
/// Returns `(inner_size, size)`
|
||||
fn sizes(&mut self, constraint: Vec2) -> (Vec2, Vec2) {
|
||||
// Attempt 1: try without scrollbars
|
||||
let (inner_size, size, scrollable) =
|
||||
self.sizes_when_scrolling(constraint, XY::new(false, false));
|
||||
|
||||
@ -308,6 +317,19 @@ where
|
||||
let steps = (available + (1, 1)).saturating_sub(lengths);
|
||||
steps * self.offset / (self.inner_size + (1, 1) - available)
|
||||
}
|
||||
|
||||
fn compute_inner_size(&mut self, constraint: Vec2) {
|
||||
if self
|
||||
.size_cache
|
||||
.map(|cache| cache.zip_map(constraint, SizeCache::accept).both())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
let (inner_size, _) = self.sizes(constraint);
|
||||
self.inner_size = inner_size;
|
||||
self.size_cache = Some(SizeCache::build(inner_size, constraint));
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> View for ScrollView<V>
|
||||
@ -524,25 +546,21 @@ where
|
||||
// Size is final now
|
||||
self.last_size = size;
|
||||
|
||||
let (inner_size, _) = self.sizes(size);
|
||||
|
||||
// Ask one more time
|
||||
self.inner_size = inner_size;
|
||||
self.compute_inner_size(size);
|
||||
|
||||
self.inner.layout(self.inner_size);
|
||||
|
||||
// The offset cannot be more than content - available
|
||||
// The offset cannot be more than (content - available)
|
||||
self.offset = self
|
||||
.offset
|
||||
.or_min(inner_size.saturating_sub(self.available_size()));
|
||||
.or_min(self.inner_size.saturating_sub(self.available_size()));
|
||||
}
|
||||
|
||||
fn needs_relayout(&self) -> bool {
|
||||
self.inner.needs_relayout()
|
||||
self.inner.needs_relayout() || self.size_cache.is_none()
|
||||
}
|
||||
|
||||
fn required_size(&mut self, constraint: Vec2) -> Vec2 {
|
||||
// Attempt 1: try without scrollbars
|
||||
let (_, size) = self.sizes(constraint);
|
||||
|
||||
size
|
||||
|
@ -1,19 +1,18 @@
|
||||
use align::*;
|
||||
use direction::Direction;
|
||||
use event::*;
|
||||
use owning_ref::{ArcRef, OwningHandle};
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
use theme::Effect;
|
||||
|
||||
use owning_ref::{ArcRef, OwningHandle};
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
use align::*;
|
||||
use direction::Direction;
|
||||
use event::*;
|
||||
use theme::Effect;
|
||||
use utils::lines::spans::{LinesIterator, Row};
|
||||
use utils::markup::StyledString;
|
||||
use vec::Vec2;
|
||||
use view::{ScrollBase, ScrollStrategy, SizeCache, View};
|
||||
use Printer;
|
||||
use With;
|
||||
use XY;
|
||||
use {Printer, Vec2, With, XY};
|
||||
|
||||
/// Provides access to the content of a [`TextView`].
|
||||
///
|
||||
@ -382,6 +381,8 @@ impl TextView {
|
||||
return;
|
||||
}
|
||||
|
||||
eprintln!("RECOMPUTE");
|
||||
|
||||
// Completely bust the cache
|
||||
// Just in case we fail, we don't want to leave a bad cache.
|
||||
content.size_cache = None;
|
||||
|
Loading…
Reference in New Issue
Block a user