mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +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
|
/// threads, ncurses' signal handler is confused and he can't keep track of
|
||||||
/// the terminal size. Poor ncurses.
|
/// the terminal size. Poor ncurses.
|
||||||
fn terminal_size() -> Vec2 {
|
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> {
|
fn split_i32(code: i32) -> Vec<u8> {
|
||||||
|
@ -126,5 +126,6 @@ pub mod backend;
|
|||||||
|
|
||||||
pub use cursive::{CbFunc, Cursive, ScreenId};
|
pub use cursive::{CbFunc, Cursive, ScreenId};
|
||||||
pub use printer::Printer;
|
pub use printer::Printer;
|
||||||
|
pub use vec::Vec2;
|
||||||
pub use with::With;
|
pub use with::With;
|
||||||
pub use xy::XY;
|
pub use xy::XY;
|
||||||
|
@ -52,9 +52,6 @@ impl SizeCache {
|
|||||||
/// * `size` must fit inside `req`.
|
/// * `size` must fit inside `req`.
|
||||||
/// * for each dimension, `constrained = (size == req)`
|
/// * for each dimension, `constrained = (size == req)`
|
||||||
pub fn build(size: Vec2, req: Vec2) -> XY<Self> {
|
pub fn build(size: Vec2, req: Vec2) -> XY<Self> {
|
||||||
XY::new(
|
size.zip_map(req, |size, req| SizeCache::new(size, size >= req))
|
||||||
SizeCache::new(size.x, size.x >= req.x),
|
|
||||||
SizeCache::new(size.y, size.y >= req.y),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,57 +1,57 @@
|
|||||||
|
use std::cmp::min;
|
||||||
|
|
||||||
use direction::{Direction, Orientation};
|
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 vec::Vec2;
|
use view::{Selector, SizeCache, View};
|
||||||
use view::{Selector, View};
|
use {Printer, Vec2, With, XY};
|
||||||
use xy::XY;
|
|
||||||
use Printer;
|
|
||||||
use With;
|
|
||||||
|
|
||||||
use std::cmp::min;
|
|
||||||
|
|
||||||
/// Wraps a view in a scrollable area.
|
/// Wraps a view in a scrollable area.
|
||||||
pub struct ScrollView<V> {
|
pub struct ScrollView<V> {
|
||||||
// The wrapped view.
|
/// The wrapped view.
|
||||||
inner: V,
|
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,
|
inner_size: Vec2,
|
||||||
|
|
||||||
// Offset into the inner view.
|
/// Offset into the inner view.
|
||||||
//
|
///
|
||||||
// Our `(0,0)` will be inner's `offset`
|
/// Our `(0,0)` will be inner's `offset`
|
||||||
offset: Vec2,
|
offset: Vec2,
|
||||||
|
|
||||||
// What was our own size last time we checked.
|
/// What was our own size last time we checked.
|
||||||
//
|
///
|
||||||
// This includes scrollbars, if any.
|
/// This includes scrollbars, if any.
|
||||||
last_size: Vec2,
|
last_size: Vec2,
|
||||||
|
|
||||||
// Are we scrollable in each direction?
|
/// Are we scrollable in each direction?
|
||||||
enabled: XY<bool>,
|
enabled: XY<bool>,
|
||||||
|
|
||||||
// Should we show scrollbars?
|
/// Should we show scrollbars?
|
||||||
//
|
///
|
||||||
// Even if this is true, no scrollbar will be printed if we don't need to
|
/// Even if this is true, no scrollbar will be printed if we don't need to
|
||||||
// scroll.
|
/// scroll.
|
||||||
//
|
///
|
||||||
// TODO: have an option to always show the scrollbar.
|
/// TODO: have an option to always show the scrollbar.
|
||||||
// TODO: have an option to show scrollbar on top/left.
|
/// TODO: have an option to show scrollbar on top/left.
|
||||||
show_scrollbars: bool,
|
show_scrollbars: bool,
|
||||||
|
|
||||||
// How much padding should be between content and scrollbar?
|
/// How much padding should be between content and scrollbar?
|
||||||
scrollbar_padding: Vec2,
|
scrollbar_padding: Vec2,
|
||||||
|
|
||||||
/// Initial position of the cursor when dragging.
|
/// Initial position of the cursor when dragging.
|
||||||
thumb_grab: Option<(Orientation, usize)>,
|
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> {
|
impl<V> ScrollView<V> {
|
||||||
/// Creates a new ScrollView around `view`.
|
/// Creates a new ScrollView around `view`.
|
||||||
pub fn new(view: V) -> Self {
|
pub fn new(inner: V) -> Self {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
inner: view,
|
inner,
|
||||||
inner_size: Vec2::zero(),
|
inner_size: Vec2::zero(),
|
||||||
offset: Vec2::zero(),
|
offset: Vec2::zero(),
|
||||||
last_size: Vec2::zero(),
|
last_size: Vec2::zero(),
|
||||||
@ -59,6 +59,7 @@ impl<V> ScrollView<V> {
|
|||||||
show_scrollbars: true,
|
show_scrollbars: true,
|
||||||
scrollbar_padding: Vec2::new(1, 0),
|
scrollbar_padding: Vec2::new(1, 0),
|
||||||
thumb_grab: None,
|
thumb_grab: None,
|
||||||
|
size_cache: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,6 +82,7 @@ impl<V> ScrollView<V> {
|
|||||||
/// Defaults to `true`.
|
/// Defaults to `true`.
|
||||||
pub fn set_scroll_y(&mut self, enabled: bool) {
|
pub fn set_scroll_y(&mut self, enabled: bool) {
|
||||||
self.enabled.y = enabled;
|
self.enabled.y = enabled;
|
||||||
|
self.invalidate_cache();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Controls whether this view can scroll horizontally.
|
/// Controls whether this view can scroll horizontally.
|
||||||
@ -88,6 +90,7 @@ impl<V> ScrollView<V> {
|
|||||||
/// Defaults to `false`.
|
/// Defaults to `false`.
|
||||||
pub fn set_scroll_x(&mut self, enabled: bool) {
|
pub fn set_scroll_x(&mut self, enabled: bool) {
|
||||||
self.enabled.x = enabled;
|
self.enabled.x = enabled;
|
||||||
|
self.invalidate_cache();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Controls whether this view can scroll vertically.
|
/// Controls whether this view can scroll vertically.
|
||||||
@ -134,6 +137,11 @@ impl<V> ScrollView<V> {
|
|||||||
self.set_offset((max_x, curr_y));
|
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.
|
/// Returns for each axis if we are scrolling.
|
||||||
fn is_scrolling(&self) -> XY<bool> {
|
fn is_scrolling(&self) -> XY<bool> {
|
||||||
self.inner_size.zip_map(self.last_size, |i, s| i > s)
|
self.inner_size.zip_map(self.last_size, |i, s| i > s)
|
||||||
@ -268,6 +276,7 @@ where
|
|||||||
///
|
///
|
||||||
/// Returns `(inner_size, size)`
|
/// Returns `(inner_size, size)`
|
||||||
fn sizes(&mut self, constraint: Vec2) -> (Vec2, Vec2) {
|
fn sizes(&mut self, constraint: Vec2) -> (Vec2, Vec2) {
|
||||||
|
// Attempt 1: try without scrollbars
|
||||||
let (inner_size, size, scrollable) =
|
let (inner_size, size, scrollable) =
|
||||||
self.sizes_when_scrolling(constraint, XY::new(false, false));
|
self.sizes_when_scrolling(constraint, XY::new(false, false));
|
||||||
|
|
||||||
@ -308,6 +317,19 @@ where
|
|||||||
let steps = (available + (1, 1)).saturating_sub(lengths);
|
let steps = (available + (1, 1)).saturating_sub(lengths);
|
||||||
steps * self.offset / (self.inner_size + (1, 1) - available)
|
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>
|
impl<V> View for ScrollView<V>
|
||||||
@ -524,25 +546,21 @@ where
|
|||||||
// Size is final now
|
// Size is final now
|
||||||
self.last_size = size;
|
self.last_size = size;
|
||||||
|
|
||||||
let (inner_size, _) = self.sizes(size);
|
self.compute_inner_size(size);
|
||||||
|
|
||||||
// Ask one more time
|
|
||||||
self.inner_size = inner_size;
|
|
||||||
|
|
||||||
self.inner.layout(self.inner_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
|
self.offset = self
|
||||||
.offset
|
.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 {
|
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 {
|
fn required_size(&mut self, constraint: Vec2) -> Vec2 {
|
||||||
// Attempt 1: try without scrollbars
|
|
||||||
let (_, size) = self.sizes(constraint);
|
let (_, size) = self.sizes(constraint);
|
||||||
|
|
||||||
size
|
size
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
use align::*;
|
|
||||||
use direction::Direction;
|
|
||||||
use event::*;
|
|
||||||
use owning_ref::{ArcRef, OwningHandle};
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::{Mutex, MutexGuard};
|
use std::sync::{Mutex, MutexGuard};
|
||||||
use theme::Effect;
|
|
||||||
|
use owning_ref::{ArcRef, OwningHandle};
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
|
use align::*;
|
||||||
|
use direction::Direction;
|
||||||
|
use event::*;
|
||||||
|
use theme::Effect;
|
||||||
use utils::lines::spans::{LinesIterator, Row};
|
use utils::lines::spans::{LinesIterator, Row};
|
||||||
use utils::markup::StyledString;
|
use utils::markup::StyledString;
|
||||||
use vec::Vec2;
|
|
||||||
use view::{ScrollBase, ScrollStrategy, SizeCache, View};
|
use view::{ScrollBase, ScrollStrategy, SizeCache, View};
|
||||||
use Printer;
|
use {Printer, Vec2, With, XY};
|
||||||
use With;
|
|
||||||
use XY;
|
|
||||||
|
|
||||||
/// Provides access to the content of a [`TextView`].
|
/// Provides access to the content of a [`TextView`].
|
||||||
///
|
///
|
||||||
@ -382,6 +381,8 @@ 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