mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-27 19:26:09 +00:00
Fix size cache in ScrollView
This commit is contained in:
parent
682fadeb1c
commit
acc1405c2a
@ -1,3 +1,4 @@
|
|||||||
|
use With;
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
use view::{SizeConstraint, View, ViewWrapper};
|
use view::{SizeConstraint, View, ViewWrapper};
|
||||||
use XY;
|
use XY;
|
||||||
@ -27,8 +28,11 @@ pub struct BoxView<T: View> {
|
|||||||
///
|
///
|
||||||
/// This means if the required size is less than the computed size,
|
/// This means if the required size is less than the computed size,
|
||||||
/// consider returning a smaller size.
|
/// consider returning a smaller size.
|
||||||
/// For instance, try to return the child's desires size.
|
/// For instance, try to return the child's desired size.
|
||||||
squishable: bool,
|
squishable: bool, // TODO: remove?
|
||||||
|
|
||||||
|
/// Set to `true` whenever we change some settings. Means we should re-layout just in case.
|
||||||
|
invalidated: bool,
|
||||||
|
|
||||||
/// The actual view we're wrapping.
|
/// The actual view we're wrapping.
|
||||||
view: T,
|
view: T,
|
||||||
@ -44,6 +48,7 @@ impl<T: View> BoxView<T> {
|
|||||||
BoxView {
|
BoxView {
|
||||||
size: (width, height).into(),
|
size: (width, height).into(),
|
||||||
squishable: false,
|
squishable: false,
|
||||||
|
invalidated: true,
|
||||||
view,
|
view,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -61,6 +66,7 @@ impl<T: View> BoxView<T> {
|
|||||||
/// Leaves the height unchanged.
|
/// Leaves the height unchanged.
|
||||||
pub fn set_width(&mut self, width: SizeConstraint) {
|
pub fn set_width(&mut self, width: SizeConstraint) {
|
||||||
self.size.x = width;
|
self.size.x = width;
|
||||||
|
self.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the height constraint for this view.
|
/// Sets the height constraint for this view.
|
||||||
@ -68,6 +74,7 @@ impl<T: View> BoxView<T> {
|
|||||||
/// Leaves the width unchanged.
|
/// Leaves the width unchanged.
|
||||||
pub fn set_height(&mut self, height: SizeConstraint) {
|
pub fn set_height(&mut self, height: SizeConstraint) {
|
||||||
self.size.y = height;
|
self.size.y = height;
|
||||||
|
self.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets `self` to be squishable.
|
/// Sets `self` to be squishable.
|
||||||
@ -78,9 +85,14 @@ impl<T: View> BoxView<T> {
|
|||||||
///
|
///
|
||||||
/// More specifically, if the available space is less than the size we
|
/// More specifically, if the available space is less than the size we
|
||||||
/// would normally ask for, return the child size.
|
/// would normally ask for, return the child size.
|
||||||
pub fn squishable(mut self) -> Self {
|
pub fn squishable(self) -> Self {
|
||||||
self.squishable = true;
|
self.with(|s| s.set_squishable(true))
|
||||||
self
|
}
|
||||||
|
|
||||||
|
/// Controls the "squishability" of `self`.
|
||||||
|
pub fn set_squishable(&mut self, squishable: bool) {
|
||||||
|
self.squishable = squishable;
|
||||||
|
self.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wraps `view` in a new `BoxView` with the given size.
|
/// Wraps `view` in a new `BoxView` with the given size.
|
||||||
@ -185,6 +197,12 @@ impl<T: View> BoxView<T> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Should be called anytime something changes.
|
||||||
|
fn invalidate(&mut self) {
|
||||||
|
self.invalidated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
inner_getters!(self.view: T);
|
inner_getters!(self.view: T);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,34 +210,42 @@ impl<T: View> ViewWrapper for BoxView<T> {
|
|||||||
wrap_impl!(self.view: T);
|
wrap_impl!(self.view: T);
|
||||||
|
|
||||||
fn wrap_required_size(&mut self, req: Vec2) -> Vec2 {
|
fn wrap_required_size(&mut self, req: Vec2) -> Vec2 {
|
||||||
|
// This is what the child will see as request.
|
||||||
let req = self.size.zip_map(req, SizeConstraint::available);
|
let req = self.size.zip_map(req, SizeConstraint::available);
|
||||||
|
|
||||||
|
// This is the size the child would like to have.
|
||||||
let child_size = self.view.required_size(req);
|
let child_size = self.view.required_size(req);
|
||||||
|
|
||||||
|
// Some of this request will be granted, but maybe not all.
|
||||||
let result = self
|
let result = self
|
||||||
.size
|
.size
|
||||||
.zip_map(child_size.zip(req), SizeConstraint::result);
|
.zip_map(child_size.zip(req), SizeConstraint::result);
|
||||||
|
|
||||||
debug!("{:?}", result);
|
debug!("{:?}", result);
|
||||||
|
|
||||||
if self.squishable {
|
if !self.squishable {
|
||||||
|
result
|
||||||
|
} else {
|
||||||
|
// When we're squishable, special behaviour:
|
||||||
|
//
|
||||||
|
|
||||||
// We respect the request if we're less or equal.
|
// We respect the request if we're less or equal.
|
||||||
let respect_req = result.zip_map(req, |res, req| res <= req);
|
let respect_req = result.zip_map(req, |res, req| res <= req);
|
||||||
result.zip_map(
|
|
||||||
respect_req.zip(child_size),
|
// If we respect the request, keep the result
|
||||||
|res, (respect, child)| {
|
// Otherwise, take the child as squish attempt.
|
||||||
if respect {
|
respect_req.select_or(result, child_size)
|
||||||
// If we respect the request, keep the result
|
|
||||||
res
|
|
||||||
} else {
|
|
||||||
// Otherwise, take the child as squish attempt.
|
|
||||||
child
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn wrap_layout(&mut self, size: Vec2) {
|
||||||
|
self.invalidated = false;
|
||||||
|
self.view.layout(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrap_needs_relayout(&self) -> bool {
|
||||||
|
self.invalidated || self.view.needs_relayout()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -23,6 +23,8 @@ pub struct Button {
|
|||||||
callback: Callback,
|
callback: Callback,
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
last_size: Vec2,
|
last_size: Vec2,
|
||||||
|
|
||||||
|
invalidated: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Button {
|
impl Button {
|
||||||
@ -46,6 +48,7 @@ impl Button {
|
|||||||
callback: Callback::from_fn(cb),
|
callback: Callback::from_fn(cb),
|
||||||
enabled: true,
|
enabled: true,
|
||||||
last_size: Vec2::zero(),
|
last_size: Vec2::zero(),
|
||||||
|
invalidated: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,11 +124,16 @@ impl Button {
|
|||||||
S: Into<String>,
|
S: Into<String>,
|
||||||
{
|
{
|
||||||
self.label = label.into();
|
self.label = label.into();
|
||||||
|
self.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn req_size(&self) -> Vec2 {
|
fn req_size(&self) -> Vec2 {
|
||||||
Vec2::new(self.label.width(), 1)
|
Vec2::new(self.label.width(), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn invalidate(&mut self) {
|
||||||
|
self.invalidated = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl View for Button {
|
impl View for Button {
|
||||||
@ -152,6 +160,7 @@ impl View for Button {
|
|||||||
|
|
||||||
fn layout(&mut self, size: Vec2) {
|
fn layout(&mut self, size: Vec2) {
|
||||||
self.last_size = size;
|
self.last_size = size;
|
||||||
|
self.invalidated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn required_size(&mut self, _: Vec2) -> Vec2 {
|
fn required_size(&mut self, _: Vec2) -> Vec2 {
|
||||||
@ -195,4 +204,8 @@ impl View for Button {
|
|||||||
|
|
||||||
Rect::from_size((offset, 0), (width, 1))
|
Rect::from_size((offset, 0), (width, 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn needs_relayout(&self) -> bool {
|
||||||
|
self.invalidated
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,6 +75,9 @@ pub struct Dialog {
|
|||||||
|
|
||||||
// How to align the buttons under the view.
|
// How to align the buttons under the view.
|
||||||
align: Align,
|
align: Align,
|
||||||
|
|
||||||
|
// `true` when we needs to relayout
|
||||||
|
invalidated: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
new_default!(Dialog);
|
new_default!(Dialog);
|
||||||
@ -98,6 +101,7 @@ impl Dialog {
|
|||||||
padding: Margins::new(1, 1, 0, 0),
|
padding: Margins::new(1, 1, 0, 0),
|
||||||
borders: Margins::new(1, 1, 1, 1),
|
borders: Margins::new(1, 1, 1, 1),
|
||||||
align: Align::top_right(),
|
align: Align::top_right(),
|
||||||
|
invalidated: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,6 +130,7 @@ impl Dialog {
|
|||||||
|
|
||||||
/// Gets mutable access to the content.
|
/// Gets mutable access to the content.
|
||||||
pub fn get_content_mut(&mut self) -> &mut View {
|
pub fn get_content_mut(&mut self) -> &mut View {
|
||||||
|
self.invalidate();
|
||||||
&mut *self.content.view
|
&mut *self.content.view
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,6 +139,7 @@ impl Dialog {
|
|||||||
/// Previous content will be dropped.
|
/// Previous content will be dropped.
|
||||||
pub fn set_content<V: View + 'static>(&mut self, view: V) {
|
pub fn set_content<V: View + 'static>(&mut self, view: V) {
|
||||||
self.content = SizedView::new(ViewBox::boxed(view));
|
self.content = SizedView::new(ViewBox::boxed(view));
|
||||||
|
self.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenient method to create a dialog with a simple text content.
|
/// Convenient method to create a dialog with a simple text content.
|
||||||
@ -164,6 +170,7 @@ impl Dialog {
|
|||||||
F: 'static + Fn(&mut Cursive),
|
F: 'static + Fn(&mut Cursive),
|
||||||
{
|
{
|
||||||
self.buttons.push(ChildButton::new(label, cb));
|
self.buttons.push(ChildButton::new(label, cb));
|
||||||
|
self.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of buttons on this dialog.
|
/// Returns the number of buttons on this dialog.
|
||||||
@ -174,6 +181,7 @@ impl Dialog {
|
|||||||
/// Removes any button from `self`.
|
/// Removes any button from `self`.
|
||||||
pub fn clear_buttons(&mut self) {
|
pub fn clear_buttons(&mut self) {
|
||||||
self.buttons.clear();
|
self.buttons.clear();
|
||||||
|
self.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a button from this dialog.
|
/// Removes a button from this dialog.
|
||||||
@ -183,6 +191,7 @@ impl Dialog {
|
|||||||
/// Panics if `i >= self.buttons_len()`.
|
/// Panics if `i >= self.buttons_len()`.
|
||||||
pub fn remove_button(&mut self, i: usize) {
|
pub fn remove_button(&mut self, i: usize) {
|
||||||
self.buttons.remove(i);
|
self.buttons.remove(i);
|
||||||
|
self.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the horizontal alignment for the buttons, if any.
|
/// Sets the horizontal alignment for the buttons, if any.
|
||||||
@ -224,6 +233,7 @@ impl Dialog {
|
|||||||
/// Sets the title of the dialog.
|
/// Sets the title of the dialog.
|
||||||
pub fn set_title<S: Into<String>>(&mut self, label: S) {
|
pub fn set_title<S: Into<String>>(&mut self, label: S) {
|
||||||
self.title = label.into();
|
self.title = label.into();
|
||||||
|
self.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the horizontal position of the title in the dialog.
|
/// Sets the horizontal position of the title in the dialog.
|
||||||
@ -271,6 +281,7 @@ impl Dialog {
|
|||||||
|
|
||||||
/// Returns an iterator on this buttons for this dialog.
|
/// Returns an iterator on this buttons for this dialog.
|
||||||
pub fn buttons_mut(&mut self) -> impl Iterator<Item = &mut Button> {
|
pub fn buttons_mut(&mut self) -> impl Iterator<Item = &mut Button> {
|
||||||
|
self.invalidate();
|
||||||
self.buttons.iter_mut().map(|b| &mut b.button.view)
|
self.buttons.iter_mut().map(|b| &mut b.button.view)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,6 +515,10 @@ impl Dialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn invalidate(&mut self) {
|
||||||
|
self.invalidated = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl View for Dialog {
|
impl View for Dialog {
|
||||||
@ -583,8 +598,11 @@ impl View for Dialog {
|
|||||||
if buttons_height > size.y {
|
if buttons_height > size.y {
|
||||||
buttons_height = size.y;
|
buttons_height = size.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.content
|
self.content
|
||||||
.layout(size.saturating_sub((0, buttons_height)));
|
.layout(size.saturating_sub((0, buttons_height)));
|
||||||
|
|
||||||
|
self.invalidated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(&mut self, event: Event) -> EventResult {
|
fn on_event(&mut self, event: Event) -> EventResult {
|
||||||
@ -627,4 +645,8 @@ impl View for Dialog {
|
|||||||
+ self.borders.top_left()
|
+ self.borders.top_left()
|
||||||
+ self.padding.top_left()
|
+ self.padding.top_left()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn needs_relayout(&self) -> bool {
|
||||||
|
self.invalidated || self.content.needs_relayout()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,4 +8,6 @@ pub struct DummyView;
|
|||||||
|
|
||||||
impl View for DummyView {
|
impl View for DummyView {
|
||||||
fn draw(&self, _: &Printer) {}
|
fn draw(&self, _: &Printer) {}
|
||||||
|
|
||||||
|
fn needs_relayout(&self) -> bool { false }
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use view::{Selector, View, ViewWrapper};
|
use view::{Selector, View, ViewWrapper};
|
||||||
|
use vec::Vec2;
|
||||||
use With;
|
use With;
|
||||||
|
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
@ -14,6 +15,7 @@ use std::any::Any;
|
|||||||
pub struct HideableView<V> {
|
pub struct HideableView<V> {
|
||||||
view: V,
|
view: V,
|
||||||
visible: bool,
|
visible: bool,
|
||||||
|
invalidated: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V> HideableView<V> {
|
impl<V> HideableView<V> {
|
||||||
@ -24,12 +26,14 @@ impl<V> HideableView<V> {
|
|||||||
HideableView {
|
HideableView {
|
||||||
view,
|
view,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
invalidated: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the visibility for this view.
|
/// Sets the visibility for this view.
|
||||||
pub fn set_visible(&mut self, visible: bool) {
|
pub fn set_visible(&mut self, visible: bool) {
|
||||||
self.visible = visible;
|
self.visible = visible;
|
||||||
|
self.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the visibility for this view to `false`.
|
/// Sets the visibility for this view to `false`.
|
||||||
@ -49,6 +53,10 @@ impl<V> HideableView<V> {
|
|||||||
self.with(Self::hide)
|
self.with(Self::hide)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn invalidate(&mut self) {
|
||||||
|
self.invalidated = true;
|
||||||
|
}
|
||||||
|
|
||||||
inner_getters!(self.view: V);
|
inner_getters!(self.view: V);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,4 +99,13 @@ impl<V: View> ViewWrapper for HideableView<V> {
|
|||||||
{
|
{
|
||||||
Ok(self.view)
|
Ok(self.view)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn wrap_layout(&mut self, size: Vec2) {
|
||||||
|
self.invalidated = false;
|
||||||
|
self.with_view_mut(|v| v.layout(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrap_needs_relayout(&self) -> bool {
|
||||||
|
self.invalidated || (self.visible && self.view.needs_relayout())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -323,11 +323,13 @@ where
|
|||||||
/// Returns `(inner_size, desired_size)`
|
/// Returns `(inner_size, desired_size)`
|
||||||
fn sizes(&mut self, constraint: Vec2, strict: bool) -> (Vec2, Vec2) {
|
fn sizes(&mut self, constraint: Vec2, strict: bool) -> (Vec2, Vec2) {
|
||||||
// First: try the cache
|
// First: try the cache
|
||||||
if self
|
if !self.inner.needs_relayout() && self
|
||||||
.size_cache
|
.size_cache
|
||||||
.map(|cache| cache.zip_map(constraint, SizeCache::accept).both())
|
.map(|cache| cache.zip_map(constraint, SizeCache::accept).both())
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
|
// eprintln!("Cache: {:?}; constraint: {:?}", self.size_cache, constraint);
|
||||||
|
|
||||||
// The new constraint shouldn't change much,
|
// The new constraint shouldn't change much,
|
||||||
// so we can re-use previous values
|
// so we can re-use previous values
|
||||||
return (
|
return (
|
||||||
|
@ -336,12 +336,6 @@ impl TextView {
|
|||||||
|
|
||||||
// Desired width
|
// Desired width
|
||||||
self.width = self.rows.iter().map(|row| row.width).max();
|
self.width = self.rows.iter().map(|row| row.width).max();
|
||||||
|
|
||||||
// The entire "virtual" size (includes all rows)
|
|
||||||
let my_size = Vec2::new(self.width.unwrap_or(0), self.rows.len());
|
|
||||||
|
|
||||||
// Build a fresh cache.
|
|
||||||
content.size_cache = Some(SizeCache::build(my_size, size));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalidates the cache, so next call will recompute everything.
|
// Invalidates the cache, so next call will recompute everything.
|
||||||
@ -390,5 +384,12 @@ impl View for TextView {
|
|||||||
// Compute the text rows.
|
// Compute the text rows.
|
||||||
self.last_size = size;
|
self.last_size = size;
|
||||||
self.compute_rows(size);
|
self.compute_rows(size);
|
||||||
|
|
||||||
|
// The entire "virtual" size (includes all rows)
|
||||||
|
let my_size = Vec2::new(self.width.unwrap_or(0), self.rows.len());
|
||||||
|
|
||||||
|
// Build a fresh cache.
|
||||||
|
let mut content = self.content.lock().unwrap();
|
||||||
|
content.size_cache = Some(SizeCache::build(my_size, size));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user