mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +00:00
Fix panics from overflow subtraction
This commit is contained in:
parent
5cf870baaf
commit
363913c68b
@ -33,9 +33,11 @@ pub struct ScrollView<V> {
|
|||||||
|
|
||||||
// Should we show scrollbars?
|
// Should we show scrollbars?
|
||||||
//
|
//
|
||||||
// Even if this is true, no scrollbar will be printed if we don't need to scroll.
|
// Even if this is true, no scrollbar will be printed if we don't need to
|
||||||
|
// scroll.
|
||||||
//
|
//
|
||||||
// Could be an enum {Never, Auto, Always}
|
// TODO: have an option to always show the scrollbar.
|
||||||
|
// 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?
|
||||||
@ -126,7 +128,7 @@ impl<V> ScrollView<V> {
|
|||||||
|
|
||||||
/// Returns the size available for the child view.
|
/// Returns the size available for the child view.
|
||||||
fn available_size(&self) -> Vec2 {
|
fn available_size(&self) -> Vec2 {
|
||||||
self.last_size - self.scrollbar_size()
|
self.last_size.saturating_sub(self.scrollbar_size())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,8 +161,9 @@ where
|
|||||||
);
|
);
|
||||||
|
|
||||||
// On non-scrolling axis, give inner_size the available space instead.
|
// On non-scrolling axis, give inner_size the available space instead.
|
||||||
let inner_size =
|
let inner_size = self
|
||||||
self.enabled.select_or(inner_size, size - scrollbar_size);
|
.enabled
|
||||||
|
.select_or(inner_size, size.saturating_sub(scrollbar_size));
|
||||||
|
|
||||||
let new_scrollable = inner_size.zip_map(size, |i, s| i > s);
|
let new_scrollable = inner_size.zip_map(size, |i, s| i > s);
|
||||||
|
|
||||||
@ -171,7 +174,7 @@ where
|
|||||||
///
|
///
|
||||||
/// Returns `true` if the event was consumed.
|
/// Returns `true` if the event was consumed.
|
||||||
fn start_drag(&mut self, position: Vec2) -> bool {
|
fn start_drag(&mut self, position: Vec2) -> bool {
|
||||||
let scrollbar_pos = self.last_size - (1, 1);
|
let scrollbar_pos = self.last_size.saturating_sub((1, 1));
|
||||||
|
|
||||||
let grabbed = scrollbar_pos.zip_map(position, |s, p| s == p);
|
let grabbed = scrollbar_pos.zip_map(position, |s, p| s == p);
|
||||||
|
|
||||||
@ -180,18 +183,19 @@ where
|
|||||||
|
|
||||||
// See if we grabbed one of the scrollbars
|
// See if we grabbed one of the scrollbars
|
||||||
for (orientation, pos, length, offset) in
|
for (orientation, pos, length, offset) in
|
||||||
XY::zip4(Orientation::pair(), position, lengths, offsets).zip(grabbed.swap())
|
XY::zip4(Orientation::pair(), position, lengths, offsets)
|
||||||
|
.zip(grabbed.swap())
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|&(_, grab)| grab)
|
.filter(|&(_, grab)| grab)
|
||||||
.map(|(x, _)| x)
|
.map(|(x, _)| x)
|
||||||
{
|
{
|
||||||
|
|
||||||
if pos >= offset && pos < offset + length {
|
if pos >= offset && pos < offset + length {
|
||||||
// We grabbed the thumb! Now scroll from that position.
|
// We grabbed the thumb! Now scroll from that position.
|
||||||
self.thumb_grab = Some((orientation, pos - offset));
|
self.thumb_grab = Some((orientation, pos - offset));
|
||||||
} else {
|
} else {
|
||||||
// We hit the scrollbar, outside of the thumb. Let's move the middle there.
|
// We hit the scrollbar, outside of the thumb.
|
||||||
self.thumb_grab = Some((orientation, (length-1)/2));
|
// Let's move the middle there.
|
||||||
|
self.thumb_grab = Some((orientation, (length - 1) / 2));
|
||||||
self.drag(position);
|
self.drag(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -214,10 +218,12 @@ where
|
|||||||
let lengths = self.scrollbar_thumb_lengths();
|
let lengths = self.scrollbar_thumb_lengths();
|
||||||
let available = self.available_size();
|
let available = self.available_size();
|
||||||
|
|
||||||
// The new offset is thumb_pos * (content + 1 - available) / (available + 1 - thumb size)
|
// The new offset is:
|
||||||
let new_offset = (self.inner_size + (1, 1) - available) * thumb_pos
|
// thumb_pos * (content + 1 - available) / (available + 1 - thumb size)
|
||||||
/ (available + (1, 1) - lengths);
|
let new_offset = (self.inner_size + (1, 1)).saturating_sub(available)
|
||||||
let max_offset = self.inner_size - self.available_size();
|
* thumb_pos
|
||||||
|
/ (available + (1, 1)).saturating_sub(lengths);
|
||||||
|
let max_offset = self.inner_size.saturating_sub(self.available_size());
|
||||||
self.offset
|
self.offset
|
||||||
.set_axis_from(orientation, &new_offset.or_min(max_offset));
|
.set_axis_from(orientation, &new_offset.or_min(max_offset));
|
||||||
}
|
}
|
||||||
@ -235,7 +241,8 @@ where
|
|||||||
|
|
||||||
// If we need to add scrollbars, the available size will change.
|
// If we need to add scrollbars, the available size will change.
|
||||||
if scrollable.any() && self.show_scrollbars {
|
if scrollable.any() && self.show_scrollbars {
|
||||||
// Attempt 2: he wants to scroll? Sure! Try again with some space for the scrollbar.
|
// Attempt 2: he wants to scroll? Sure!
|
||||||
|
// Try again with some space for the scrollbar.
|
||||||
let (inner_size, size, new_scrollable) =
|
let (inner_size, size, new_scrollable) =
|
||||||
self.sizes_when_scrolling(constraint, scrollable);
|
self.sizes_when_scrolling(constraint, scrollable);
|
||||||
if scrollable != new_scrollable {
|
if scrollable != new_scrollable {
|
||||||
@ -266,7 +273,7 @@ where
|
|||||||
fn scrollbar_thumb_offsets(&self, lengths: Vec2) -> Vec2 {
|
fn scrollbar_thumb_offsets(&self, lengths: Vec2) -> Vec2 {
|
||||||
let available = self.available_size();
|
let available = self.available_size();
|
||||||
// The number of steps is 1 + the "extra space"
|
// The number of steps is 1 + the "extra space"
|
||||||
let steps = available - lengths + (1, 1);
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -296,12 +303,19 @@ where
|
|||||||
XY::zip5(lengths, offsets, size, line_c, Orientation::pair()).run_if(
|
XY::zip5(lengths, offsets, size, line_c, Orientation::pair()).run_if(
|
||||||
scrolling,
|
scrolling,
|
||||||
|(length, offset, size, c, orientation)| {
|
|(length, offset, size, c, orientation)| {
|
||||||
let start = (printer.size - (1, 1)).with_axis(orientation, 0);
|
let start = printer
|
||||||
|
.size
|
||||||
|
.saturating_sub((1, 1))
|
||||||
|
.with_axis(orientation, 0);
|
||||||
let offset = orientation.make_vec(offset, 0);
|
let offset = orientation.make_vec(offset, 0);
|
||||||
|
|
||||||
printer.print_line(orientation, start, size, c);
|
printer.print_line(orientation, start, size, c);
|
||||||
|
|
||||||
let thumb_c = if self.thumb_grab.map(|(o, _)| o == orientation).unwrap_or(false) {
|
let thumb_c = if self
|
||||||
|
.thumb_grab
|
||||||
|
.map(|(o, _)| o == orientation)
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
" "
|
" "
|
||||||
} else {
|
} else {
|
||||||
"▒"
|
"▒"
|
||||||
@ -311,14 +325,14 @@ where
|
|||||||
orientation,
|
orientation,
|
||||||
start + offset,
|
start + offset,
|
||||||
length,
|
length,
|
||||||
thumb_c,
|
thumb_c,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if scrolling.both() {
|
if scrolling.both() {
|
||||||
printer.print((printer.size.x - 1, printer.size.y - 1), "╳");
|
printer.print(printer.size.saturating_sub((1, 1)), "╳");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw content
|
// Draw content
|
||||||
@ -340,7 +354,8 @@ thumb_c,
|
|||||||
EventResult::Ignored => {
|
EventResult::Ignored => {
|
||||||
// If it's an arrow, try to scroll in the given direction.
|
// If it's an arrow, try to scroll in the given direction.
|
||||||
// If it's a mouse scroll, try to scroll as well.
|
// If it's a mouse scroll, try to scroll as well.
|
||||||
// Also allow Ctrl+arrow to move the view without moving selection.
|
// Also allow Ctrl+arrow to move the view,
|
||||||
|
// but not the selection.
|
||||||
match event {
|
match event {
|
||||||
Event::Mouse {
|
Event::Mouse {
|
||||||
event: MouseEvent::WheelUp,
|
event: MouseEvent::WheelUp,
|
||||||
@ -457,7 +472,10 @@ thumb_c,
|
|||||||
|
|
||||||
self.inner.layout(self.inner_size);
|
self.inner.layout(self.inner_size);
|
||||||
|
|
||||||
// TODO: Refresh offset if needed!
|
// The offset cannot be more than content - available
|
||||||
|
self.offset = self
|
||||||
|
.offset
|
||||||
|
.or_min(inner_size.saturating_sub(self.available_size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn needs_relayout(&self) -> bool {
|
fn needs_relayout(&self) -> bool {
|
||||||
|
Loading…
Reference in New Issue
Block a user