mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +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 view::{SizeConstraint, View, ViewWrapper};
|
||||
use XY;
|
||||
@ -27,8 +28,11 @@ pub struct BoxView<T: View> {
|
||||
///
|
||||
/// This means if the required size is less than the computed size,
|
||||
/// consider returning a smaller size.
|
||||
/// For instance, try to return the child's desires size.
|
||||
squishable: bool,
|
||||
/// For instance, try to return the child's desired size.
|
||||
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.
|
||||
view: T,
|
||||
@ -44,6 +48,7 @@ impl<T: View> BoxView<T> {
|
||||
BoxView {
|
||||
size: (width, height).into(),
|
||||
squishable: false,
|
||||
invalidated: true,
|
||||
view,
|
||||
}
|
||||
}
|
||||
@ -61,6 +66,7 @@ impl<T: View> BoxView<T> {
|
||||
/// Leaves the height unchanged.
|
||||
pub fn set_width(&mut self, width: SizeConstraint) {
|
||||
self.size.x = width;
|
||||
self.invalidate();
|
||||
}
|
||||
|
||||
/// Sets the height constraint for this view.
|
||||
@ -68,6 +74,7 @@ impl<T: View> BoxView<T> {
|
||||
/// Leaves the width unchanged.
|
||||
pub fn set_height(&mut self, height: SizeConstraint) {
|
||||
self.size.y = height;
|
||||
self.invalidate();
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// would normally ask for, return the child size.
|
||||
pub fn squishable(mut self) -> Self {
|
||||
self.squishable = true;
|
||||
self
|
||||
pub fn squishable(self) -> Self {
|
||||
self.with(|s| s.set_squishable(true))
|
||||
}
|
||||
|
||||
/// 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.
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -192,33 +210,41 @@ impl<T: View> ViewWrapper for BoxView<T> {
|
||||
wrap_impl!(self.view: T);
|
||||
|
||||
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);
|
||||
|
||||
// This is the size the child would like to have.
|
||||
let child_size = self.view.required_size(req);
|
||||
|
||||
// Some of this request will be granted, but maybe not all.
|
||||
let result = self
|
||||
.size
|
||||
.zip_map(child_size.zip(req), SizeConstraint::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.
|
||||
let respect_req = result.zip_map(req, |res, req| res <= req);
|
||||
result.zip_map(
|
||||
respect_req.zip(child_size),
|
||||
|res, (respect, child)| {
|
||||
if respect {
|
||||
|
||||
// If we respect the request, keep the result
|
||||
res
|
||||
} else {
|
||||
// Otherwise, take the child as squish attempt.
|
||||
child
|
||||
respect_req.select_or(result, child_size)
|
||||
}
|
||||
},
|
||||
)
|
||||
} 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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,8 @@ pub struct Button {
|
||||
callback: Callback,
|
||||
enabled: bool,
|
||||
last_size: Vec2,
|
||||
|
||||
invalidated: bool,
|
||||
}
|
||||
|
||||
impl Button {
|
||||
@ -46,6 +48,7 @@ impl Button {
|
||||
callback: Callback::from_fn(cb),
|
||||
enabled: true,
|
||||
last_size: Vec2::zero(),
|
||||
invalidated: true,
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,11 +124,16 @@ impl Button {
|
||||
S: Into<String>,
|
||||
{
|
||||
self.label = label.into();
|
||||
self.invalidate();
|
||||
}
|
||||
|
||||
fn req_size(&self) -> Vec2 {
|
||||
Vec2::new(self.label.width(), 1)
|
||||
}
|
||||
|
||||
fn invalidate(&mut self) {
|
||||
self.invalidated = true;
|
||||
}
|
||||
}
|
||||
|
||||
impl View for Button {
|
||||
@ -152,6 +160,7 @@ impl View for Button {
|
||||
|
||||
fn layout(&mut self, size: Vec2) {
|
||||
self.last_size = size;
|
||||
self.invalidated = false;
|
||||
}
|
||||
|
||||
fn required_size(&mut self, _: Vec2) -> Vec2 {
|
||||
@ -195,4 +204,8 @@ impl View for Button {
|
||||
|
||||
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.
|
||||
align: Align,
|
||||
|
||||
// `true` when we needs to relayout
|
||||
invalidated: bool,
|
||||
}
|
||||
|
||||
new_default!(Dialog);
|
||||
@ -98,6 +101,7 @@ impl Dialog {
|
||||
padding: Margins::new(1, 1, 0, 0),
|
||||
borders: Margins::new(1, 1, 1, 1),
|
||||
align: Align::top_right(),
|
||||
invalidated: true,
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,6 +130,7 @@ impl Dialog {
|
||||
|
||||
/// Gets mutable access to the content.
|
||||
pub fn get_content_mut(&mut self) -> &mut View {
|
||||
self.invalidate();
|
||||
&mut *self.content.view
|
||||
}
|
||||
|
||||
@ -134,6 +139,7 @@ impl Dialog {
|
||||
/// Previous content will be dropped.
|
||||
pub fn set_content<V: View + 'static>(&mut self, view: V) {
|
||||
self.content = SizedView::new(ViewBox::boxed(view));
|
||||
self.invalidate();
|
||||
}
|
||||
|
||||
/// Convenient method to create a dialog with a simple text content.
|
||||
@ -164,6 +170,7 @@ impl Dialog {
|
||||
F: 'static + Fn(&mut Cursive),
|
||||
{
|
||||
self.buttons.push(ChildButton::new(label, cb));
|
||||
self.invalidate();
|
||||
}
|
||||
|
||||
/// Returns the number of buttons on this dialog.
|
||||
@ -174,6 +181,7 @@ impl Dialog {
|
||||
/// Removes any button from `self`.
|
||||
pub fn clear_buttons(&mut self) {
|
||||
self.buttons.clear();
|
||||
self.invalidate();
|
||||
}
|
||||
|
||||
/// Removes a button from this dialog.
|
||||
@ -183,6 +191,7 @@ impl Dialog {
|
||||
/// Panics if `i >= self.buttons_len()`.
|
||||
pub fn remove_button(&mut self, i: usize) {
|
||||
self.buttons.remove(i);
|
||||
self.invalidate();
|
||||
}
|
||||
|
||||
/// Sets the horizontal alignment for the buttons, if any.
|
||||
@ -224,6 +233,7 @@ impl Dialog {
|
||||
/// Sets the title of the dialog.
|
||||
pub fn set_title<S: Into<String>>(&mut self, label: S) {
|
||||
self.title = label.into();
|
||||
self.invalidate();
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn buttons_mut(&mut self) -> impl Iterator<Item = &mut Button> {
|
||||
self.invalidate();
|
||||
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 {
|
||||
@ -583,8 +598,11 @@ impl View for Dialog {
|
||||
if buttons_height > size.y {
|
||||
buttons_height = size.y;
|
||||
}
|
||||
|
||||
self.content
|
||||
.layout(size.saturating_sub((0, buttons_height)));
|
||||
|
||||
self.invalidated = false;
|
||||
}
|
||||
|
||||
fn on_event(&mut self, event: Event) -> EventResult {
|
||||
@ -627,4 +645,8 @@ impl View for Dialog {
|
||||
+ self.borders.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 {
|
||||
fn draw(&self, _: &Printer) {}
|
||||
|
||||
fn needs_relayout(&self) -> bool { false }
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use view::{Selector, View, ViewWrapper};
|
||||
use vec::Vec2;
|
||||
use With;
|
||||
|
||||
use std::any::Any;
|
||||
@ -14,6 +15,7 @@ use std::any::Any;
|
||||
pub struct HideableView<V> {
|
||||
view: V,
|
||||
visible: bool,
|
||||
invalidated: bool,
|
||||
}
|
||||
|
||||
impl<V> HideableView<V> {
|
||||
@ -24,12 +26,14 @@ impl<V> HideableView<V> {
|
||||
HideableView {
|
||||
view,
|
||||
visible: true,
|
||||
invalidated: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the visibility for this view.
|
||||
pub fn set_visible(&mut self, visible: bool) {
|
||||
self.visible = visible;
|
||||
self.invalidate();
|
||||
}
|
||||
|
||||
/// Sets the visibility for this view to `false`.
|
||||
@ -49,6 +53,10 @@ impl<V> HideableView<V> {
|
||||
self.with(Self::hide)
|
||||
}
|
||||
|
||||
fn invalidate(&mut self) {
|
||||
self.invalidated = true;
|
||||
}
|
||||
|
||||
inner_getters!(self.view: V);
|
||||
}
|
||||
|
||||
@ -91,4 +99,13 @@ impl<V: View> ViewWrapper for HideableView<V> {
|
||||
{
|
||||
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)`
|
||||
fn sizes(&mut self, constraint: Vec2, strict: bool) -> (Vec2, Vec2) {
|
||||
// First: try the cache
|
||||
if self
|
||||
if !self.inner.needs_relayout() && self
|
||||
.size_cache
|
||||
.map(|cache| cache.zip_map(constraint, SizeCache::accept).both())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
// eprintln!("Cache: {:?}; constraint: {:?}", self.size_cache, constraint);
|
||||
|
||||
// The new constraint shouldn't change much,
|
||||
// so we can re-use previous values
|
||||
return (
|
||||
|
@ -336,12 +336,6 @@ impl TextView {
|
||||
|
||||
// Desired width
|
||||
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.
|
||||
@ -390,5 +384,12 @@ impl View for TextView {
|
||||
// Compute the text rows.
|
||||
self.last_size = 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