mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-24 01:46:31 +00:00
Move event handling outside of scroll::Core
This commit is contained in:
parent
31b8e1f515
commit
0a66978d7f
@ -17,6 +17,11 @@ use crate::XY;
|
|||||||
/// [`XY`]: crate::XY
|
/// [`XY`]: crate::XY
|
||||||
pub type Vec2 = XY<usize>;
|
pub type Vec2 = XY<usize>;
|
||||||
|
|
||||||
|
/// A signed 2D quantity, in cells.
|
||||||
|
///
|
||||||
|
/// Usually represents an offset.
|
||||||
|
pub type Vec2i = XY<isize>;
|
||||||
|
|
||||||
impl<T: PartialOrd> PartialOrd for XY<T> {
|
impl<T: PartialOrd> PartialOrd for XY<T> {
|
||||||
/// `a < b` <=> `a.x < b.x && a.y < b.y`
|
/// `a < b` <=> `a.x < b.x && a.y < b.y`
|
||||||
fn partial_cmp(&self, other: &XY<T>) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &XY<T>) -> Option<Ordering> {
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
|
|
||||||
use crate::direction::Orientation;
|
use crate::{
|
||||||
use crate::event::{AnyCb, Event, EventResult, Key, MouseButton, MouseEvent};
|
direction::Orientation,
|
||||||
use crate::printer::Printer;
|
event::{AnyCb, Event, EventResult, Key, MouseButton, MouseEvent},
|
||||||
use crate::rect::Rect;
|
printer::Printer,
|
||||||
use crate::theme::ColorStyle;
|
rect::Rect,
|
||||||
use crate::view::{ScrollStrategy, Selector, SizeCache};
|
theme::ColorStyle,
|
||||||
use crate::with::With;
|
view::{ScrollStrategy, Selector, SizeCache},
|
||||||
use crate::Vec2;
|
with::With,
|
||||||
use crate::XY;
|
Vec2, XY,
|
||||||
|
};
|
||||||
|
|
||||||
/// Describes an item with a scroll core.
|
/// Describes an item with a scroll core.
|
||||||
///
|
///
|
||||||
@ -217,137 +218,6 @@ impl Core {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle an event after processing by the content.
|
|
||||||
pub fn on_inner_event(
|
|
||||||
&mut self,
|
|
||||||
event: Event,
|
|
||||||
inner_result: EventResult,
|
|
||||||
important_area: Rect,
|
|
||||||
) -> EventResult {
|
|
||||||
match inner_result {
|
|
||||||
EventResult::Ignored => {
|
|
||||||
// The view ignored the event, so we're free to use it.
|
|
||||||
|
|
||||||
// If it's an arrow, try to scroll in the given direction.
|
|
||||||
// If it's a mouse scroll, try to scroll as well.
|
|
||||||
// Also allow Ctrl+arrow to move the view,
|
|
||||||
// without affecting the selection.
|
|
||||||
match event {
|
|
||||||
Event::Mouse {
|
|
||||||
event: MouseEvent::WheelUp,
|
|
||||||
..
|
|
||||||
} if self.enabled.y && self.offset.y > 0 => {
|
|
||||||
self.offset.y = self.offset.y.saturating_sub(3);
|
|
||||||
}
|
|
||||||
Event::Mouse {
|
|
||||||
event: MouseEvent::WheelDown,
|
|
||||||
..
|
|
||||||
} if self.enabled.y
|
|
||||||
&& (self.offset.y + self.last_available_size().y
|
|
||||||
< self.inner_size.y) =>
|
|
||||||
{
|
|
||||||
self.offset.y = min(
|
|
||||||
self.inner_size
|
|
||||||
.y
|
|
||||||
.saturating_sub(self.last_available_size().y),
|
|
||||||
self.offset.y + 3,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Event::Mouse {
|
|
||||||
event: MouseEvent::Press(MouseButton::Left),
|
|
||||||
position,
|
|
||||||
offset,
|
|
||||||
} if self.show_scrollbars
|
|
||||||
&& position
|
|
||||||
.checked_sub(offset)
|
|
||||||
.map(|position| self.start_drag(position))
|
|
||||||
.unwrap_or(false) =>
|
|
||||||
{
|
|
||||||
// Just consume the event.
|
|
||||||
}
|
|
||||||
Event::Mouse {
|
|
||||||
event: MouseEvent::Hold(MouseButton::Left),
|
|
||||||
position,
|
|
||||||
offset,
|
|
||||||
} if self.show_scrollbars => {
|
|
||||||
let position = position.saturating_sub(offset);
|
|
||||||
self.drag(position);
|
|
||||||
}
|
|
||||||
Event::Mouse {
|
|
||||||
event: MouseEvent::Release(MouseButton::Left),
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
self.release_grab();
|
|
||||||
}
|
|
||||||
Event::Key(Key::Home) if self.enabled.any() => {
|
|
||||||
self.offset =
|
|
||||||
self.enabled.select_or(Vec2::zero(), self.offset);
|
|
||||||
}
|
|
||||||
Event::Key(Key::End) if self.enabled.any() => {
|
|
||||||
let max_offset = self
|
|
||||||
.inner_size
|
|
||||||
.saturating_sub(self.last_available_size());
|
|
||||||
self.offset =
|
|
||||||
self.enabled.select_or(max_offset, self.offset);
|
|
||||||
}
|
|
||||||
Event::Ctrl(Key::Up) | Event::Key(Key::Up)
|
|
||||||
if self.enabled.y && self.offset.y > 0 =>
|
|
||||||
{
|
|
||||||
self.offset.y -= 1;
|
|
||||||
}
|
|
||||||
Event::Key(Key::PageUp)
|
|
||||||
if self.enabled.y && self.offset.y > 0 =>
|
|
||||||
{
|
|
||||||
self.offset.y = self.offset.y.saturating_sub(5);
|
|
||||||
}
|
|
||||||
Event::Key(Key::PageDown)
|
|
||||||
if self.enabled.y
|
|
||||||
&& (self.offset.y
|
|
||||||
+ self.last_available_size().y
|
|
||||||
< self.inner_size.y) =>
|
|
||||||
{
|
|
||||||
// No `min` check here - we allow going over the edge.
|
|
||||||
self.offset.y += 5;
|
|
||||||
}
|
|
||||||
Event::Ctrl(Key::Down) | Event::Key(Key::Down)
|
|
||||||
if self.enabled.y
|
|
||||||
&& (self.offset.y
|
|
||||||
+ self.last_available_size().y
|
|
||||||
< self.inner_size.y) =>
|
|
||||||
{
|
|
||||||
self.offset.y += 1;
|
|
||||||
}
|
|
||||||
Event::Ctrl(Key::Left) | Event::Key(Key::Left)
|
|
||||||
if self.enabled.x && self.offset.x > 0 =>
|
|
||||||
{
|
|
||||||
self.offset.x -= 1;
|
|
||||||
}
|
|
||||||
Event::Ctrl(Key::Right) | Event::Key(Key::Right)
|
|
||||||
if self.enabled.x
|
|
||||||
&& (self.offset.x
|
|
||||||
+ self.last_available_size().x
|
|
||||||
< self.inner_size.x) =>
|
|
||||||
{
|
|
||||||
self.offset.x += 1;
|
|
||||||
}
|
|
||||||
_ => return EventResult::Ignored,
|
|
||||||
};
|
|
||||||
|
|
||||||
// We just scrolled manually, so reset the scroll strategy.
|
|
||||||
self.scroll_strategy = ScrollStrategy::KeepRow;
|
|
||||||
// TODO: return callback on_scroll?
|
|
||||||
EventResult::Consumed(None)
|
|
||||||
}
|
|
||||||
other => {
|
|
||||||
// The view consumed the event. Maybe something changed?
|
|
||||||
|
|
||||||
self.scroll_to_rect(important_area);
|
|
||||||
|
|
||||||
other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Specifies the size given in a layout phase.
|
/// Specifies the size given in a layout phase.
|
||||||
pub(crate) fn set_last_size(
|
pub(crate) fn set_last_size(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -507,6 +377,7 @@ impl Core {
|
|||||||
{
|
{
|
||||||
let max_offset =
|
let max_offset =
|
||||||
self.inner_size.saturating_sub(self.last_available_size());
|
self.inner_size.saturating_sub(self.last_available_size());
|
||||||
|
|
||||||
self.offset = offset.into().or_min(max_offset);
|
self.offset = offset.into().or_min(max_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -599,32 +470,56 @@ impl Core {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Scroll by `n` cells to the left.
|
||||||
|
pub fn scroll_left(&mut self, n: usize) {
|
||||||
|
// Goal: never repeat .x (to prevent typo/confusion)
|
||||||
|
// TODO: further reduce code duplication.
|
||||||
|
let offset = self.offset.as_ref_mut().x;
|
||||||
|
*offset = offset.saturating_sub(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scroll by `n` cells to the top.
|
||||||
|
pub fn scroll_up(&mut self, n: usize) {
|
||||||
|
let offset = self.offset.as_ref_mut().y;
|
||||||
|
*offset = offset.saturating_sub(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scroll by `n` cells to the bottom.
|
||||||
|
pub fn scroll_down(&mut self, n: usize) {
|
||||||
|
let max_offset = self.max_offset();
|
||||||
|
let (offset, max) = XY::zip(self.offset.as_ref_mut(), max_offset).y;
|
||||||
|
*offset = min(max, *offset + n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Scroll by `n` cells to the right.
|
||||||
|
pub fn scroll_right(&mut self, n: usize) {
|
||||||
|
let max_offset = self.max_offset();
|
||||||
|
let (offset, max) = XY::zip(self.offset.as_ref_mut(), max_offset).x;
|
||||||
|
*offset = min(max, *offset + n);
|
||||||
|
}
|
||||||
|
|
||||||
/// Programmatically scroll to the top of the view.
|
/// Programmatically scroll to the top of the view.
|
||||||
pub fn scroll_to_top(&mut self) {
|
pub fn scroll_to_top(&mut self) {
|
||||||
let curr_x = self.offset.x;
|
self.offset.y = 0;
|
||||||
self.set_offset((curr_x, 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Programmatically scroll to the bottom of the view.
|
/// Programmatically scroll to the bottom of the view.
|
||||||
pub fn scroll_to_bottom(&mut self) {
|
pub fn scroll_to_bottom(&mut self) {
|
||||||
let max_y =
|
self.offset.y = self.max_offset().y;
|
||||||
self.inner_size.saturating_sub(self.last_available_size()).y;
|
|
||||||
let curr_x = self.offset.x;
|
|
||||||
self.set_offset((curr_x, max_y));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Programmatically scroll to the leftmost side of the view.
|
/// Programmatically scroll to the leftmost side of the view.
|
||||||
pub fn scroll_to_left(&mut self) {
|
pub fn scroll_to_left(&mut self) {
|
||||||
let curr_y = self.offset.y;
|
self.offset.x = 0;
|
||||||
self.set_offset((0, curr_y));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Programmatically scroll to the rightmost side of the view.
|
/// Programmatically scroll to the rightmost side of the view.
|
||||||
pub fn scroll_to_right(&mut self) {
|
pub fn scroll_to_right(&mut self) {
|
||||||
let max_x =
|
self.offset.x = self.max_offset().x;
|
||||||
self.inner_size.saturating_sub(self.last_available_size()).x;
|
}
|
||||||
let curr_y = self.offset.y;
|
|
||||||
self.set_offset((max_x, curr_y));
|
fn max_offset(&self) -> Vec2 {
|
||||||
|
self.inner_size.saturating_sub(self.last_available_size())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clears the cache.
|
/// Clears the cache.
|
||||||
@ -639,7 +534,7 @@ impl Core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Stops grabbing the scrollbar.
|
/// Stops grabbing the scrollbar.
|
||||||
fn release_grab(&mut self) {
|
pub fn release_grab(&mut self) {
|
||||||
self.thumb_grab = None;
|
self.thumb_grab = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -664,10 +559,45 @@ impl Core {
|
|||||||
self.last_available_size() + self.scrollbar_size()
|
self.last_available_size() + self.scrollbar_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if we can scroll up.
|
||||||
|
///
|
||||||
|
/// Returns `true` if vertical scrolling is enabled, and if we are not at
|
||||||
|
/// the top already.
|
||||||
|
pub fn can_scroll_up(&self) -> bool {
|
||||||
|
self.enabled.y && self.offset.y > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if we can scroll to the left.
|
||||||
|
///
|
||||||
|
/// Returns `true` if horizontal scrolling is enabled, and if we are not at
|
||||||
|
/// the left edge already.
|
||||||
|
pub fn can_scroll_left(&self) -> bool {
|
||||||
|
self.enabled.x && self.offset.x > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if we can scroll down.
|
||||||
|
///
|
||||||
|
/// Returns `true` if vertical scrolling is enabled, and if we are not at
|
||||||
|
/// the bottom already.
|
||||||
|
pub fn can_scroll_down(&self) -> bool {
|
||||||
|
self.enabled.y
|
||||||
|
&& (self.offset.y + self.last_available_size().y
|
||||||
|
< self.inner_size.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if we can scroll to the right.
|
||||||
|
///
|
||||||
|
/// Returns `true` if horizontal scrolling is enabled, and if we are not at
|
||||||
|
/// the right edge already.
|
||||||
|
pub fn can_scroll_right(&self) -> bool {
|
||||||
|
self.enabled.x
|
||||||
|
&& (self.offset.x + self.last_available_size().x
|
||||||
|
< self.inner_size.x)
|
||||||
|
}
|
||||||
/// Starts scrolling from the cursor position.
|
/// Starts scrolling from the cursor position.
|
||||||
///
|
///
|
||||||
/// Returns `true` if the event was consumed.
|
/// Returns `true` if the event was consumed.
|
||||||
fn start_drag(&mut self, position: Vec2) -> bool {
|
pub fn start_drag(&mut self, position: Vec2) -> bool {
|
||||||
// For each scrollbar, how far it is.
|
// For each scrollbar, how far it is.
|
||||||
let scrollbar_pos = self.last_outer_size().saturating_sub((1, 1));
|
let scrollbar_pos = self.last_outer_size().saturating_sub((1, 1));
|
||||||
let lengths = self.scrollbar_thumb_lengths();
|
let lengths = self.scrollbar_thumb_lengths();
|
||||||
@ -708,7 +638,7 @@ impl Core {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Called when a mouse drag is detected.
|
/// Called when a mouse drag is detected.
|
||||||
fn drag(&mut self, position: Vec2) {
|
pub fn drag(&mut self, position: Vec2) {
|
||||||
// Only do something if we grabbed something before.
|
// Only do something if we grabbed something before.
|
||||||
if let Some((orientation, grab)) = self.thumb_grab {
|
if let Some((orientation, grab)) = self.thumb_grab {
|
||||||
self.scroll_to_thumb(
|
self.scroll_to_thumb(
|
||||||
|
@ -2,12 +2,14 @@
|
|||||||
//!
|
//!
|
||||||
//! Most functions take a generic `Model` class, and various closures to get
|
//! Most functions take a generic `Model` class, and various closures to get
|
||||||
//! the required things from this model.
|
//! the required things from this model.
|
||||||
use crate::event::{Event, EventResult};
|
//!
|
||||||
use crate::rect::Rect;
|
use crate::{
|
||||||
use crate::view::scroll;
|
event::{Event, EventResult, Key, MouseButton, MouseEvent},
|
||||||
use crate::xy::XY;
|
rect::Rect,
|
||||||
use crate::Printer;
|
view::scroll,
|
||||||
use crate::Vec2;
|
xy::XY,
|
||||||
|
Printer, Vec2,
|
||||||
|
};
|
||||||
|
|
||||||
/// Implements `View::draw` over the `model`.
|
/// Implements `View::draw` over the `model`.
|
||||||
pub fn draw<Model, GetScroller, Draw>(
|
pub fn draw<Model, GetScroller, Draw>(
|
||||||
@ -224,7 +226,124 @@ pub fn on_event<Model: ?Sized>(
|
|||||||
} else {
|
} else {
|
||||||
EventResult::Ignored
|
EventResult::Ignored
|
||||||
};
|
};
|
||||||
|
|
||||||
|
match result {
|
||||||
|
EventResult::Ignored => {
|
||||||
|
// The view ignored the event, so we're free to use it.
|
||||||
|
|
||||||
|
// If it's an arrow, try to scroll in the given direction.
|
||||||
|
// If it's a mouse scroll, try to scroll as well.
|
||||||
|
// Also allow Ctrl+arrow to move the view,
|
||||||
|
// without affecting the selection.
|
||||||
|
match event {
|
||||||
|
Event::Mouse {
|
||||||
|
event: MouseEvent::WheelUp,
|
||||||
|
..
|
||||||
|
} if get_scroller(model).can_scroll_up() => {
|
||||||
|
get_scroller(model).scroll_up(3);
|
||||||
|
}
|
||||||
|
Event::Mouse {
|
||||||
|
event: MouseEvent::WheelDown,
|
||||||
|
..
|
||||||
|
} if get_scroller(model).can_scroll_down() => {
|
||||||
|
get_scroller(model).scroll_down(3);
|
||||||
|
}
|
||||||
|
Event::Mouse {
|
||||||
|
event: MouseEvent::Press(MouseButton::Left),
|
||||||
|
position,
|
||||||
|
offset,
|
||||||
|
} if get_scroller(model).get_show_scrollbars()
|
||||||
|
&& position
|
||||||
|
.checked_sub(offset)
|
||||||
|
.map(|position| {
|
||||||
|
get_scroller(model).start_drag(position)
|
||||||
|
})
|
||||||
|
.unwrap_or(false) =>
|
||||||
|
{
|
||||||
|
// Just consume the event.
|
||||||
|
}
|
||||||
|
Event::Mouse {
|
||||||
|
event: MouseEvent::Hold(MouseButton::Left),
|
||||||
|
position,
|
||||||
|
offset,
|
||||||
|
} if get_scroller(model).get_show_scrollbars() => {
|
||||||
|
let position = position.saturating_sub(offset);
|
||||||
|
get_scroller(model).drag(position);
|
||||||
|
}
|
||||||
|
Event::Mouse {
|
||||||
|
event: MouseEvent::Release(MouseButton::Left),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
get_scroller(model).release_grab();
|
||||||
|
}
|
||||||
|
Event::Key(Key::Home)
|
||||||
|
if get_scroller(model).is_enabled().any() =>
|
||||||
|
{
|
||||||
|
let actions: XY<fn(&mut scroll::Core)> = XY::new(
|
||||||
|
scroll::Core::scroll_to_left,
|
||||||
|
scroll::Core::scroll_to_top,
|
||||||
|
);
|
||||||
|
let scroller = get_scroller(model);
|
||||||
|
actions.run_if(scroller.is_enabled(), |a| a(scroller));
|
||||||
|
}
|
||||||
|
Event::Key(Key::End)
|
||||||
|
if get_scroller(model).is_enabled().any() =>
|
||||||
|
{
|
||||||
|
let actions: XY<fn(&mut scroll::Core)> = XY::new(
|
||||||
|
scroll::Core::scroll_to_right,
|
||||||
|
scroll::Core::scroll_to_bottom,
|
||||||
|
);
|
||||||
|
let scroller = get_scroller(model);
|
||||||
|
actions.run_if(scroller.is_enabled(), |a| a(scroller));
|
||||||
|
}
|
||||||
|
Event::Ctrl(Key::Up) | Event::Key(Key::Up)
|
||||||
|
if get_scroller(model).can_scroll_up() =>
|
||||||
|
{
|
||||||
|
get_scroller(model).scroll_up(1);
|
||||||
|
}
|
||||||
|
Event::Key(Key::PageUp)
|
||||||
|
if get_scroller(model).can_scroll_up() =>
|
||||||
|
{
|
||||||
|
get_scroller(model).scroll_up(5);
|
||||||
|
}
|
||||||
|
Event::Key(Key::PageDown)
|
||||||
|
if get_scroller(model).can_scroll_down() =>
|
||||||
|
{
|
||||||
|
// No `min` check here - we allow going over the edge.
|
||||||
|
get_scroller(model).scroll_down(5);
|
||||||
|
}
|
||||||
|
Event::Ctrl(Key::Down) | Event::Key(Key::Down)
|
||||||
|
if get_scroller(model).can_scroll_down() =>
|
||||||
|
{
|
||||||
|
get_scroller(model).scroll_down(1);
|
||||||
|
}
|
||||||
|
Event::Ctrl(Key::Left) | Event::Key(Key::Left)
|
||||||
|
if get_scroller(model).can_scroll_left() =>
|
||||||
|
{
|
||||||
|
get_scroller(model).scroll_left(1);
|
||||||
|
}
|
||||||
|
Event::Ctrl(Key::Right) | Event::Key(Key::Right)
|
||||||
|
if get_scroller(model).can_scroll_right() =>
|
||||||
|
{
|
||||||
|
get_scroller(model).scroll_right(1);
|
||||||
|
}
|
||||||
|
_ => return EventResult::Ignored,
|
||||||
|
};
|
||||||
|
|
||||||
|
// We just scrolled manually, so reset the scroll strategy.
|
||||||
|
get_scroller(model)
|
||||||
|
.set_scroll_strategy(scroll::ScrollStrategy::KeepRow);
|
||||||
|
|
||||||
|
// TODO: return callback on_scroll?
|
||||||
|
EventResult::Consumed(None)
|
||||||
|
}
|
||||||
|
other => {
|
||||||
|
// The view consumed the event. Maybe something changed?
|
||||||
let inner_size = get_scroller(model).inner_size();
|
let inner_size = get_scroller(model).inner_size();
|
||||||
let important = important_area(model, inner_size);
|
let important = important_area(model, inner_size);
|
||||||
get_scroller(model).on_inner_event(event, result, important)
|
get_scroller(model).scroll_to_rect(important);
|
||||||
|
|
||||||
|
other
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
/// Generic trait to enable chainable API
|
/// Generic trait to enable chainable API
|
||||||
pub trait With: Sized {
|
pub trait With: Sized {
|
||||||
|
/// Calls the given closure and return the result.
|
||||||
|
///
|
||||||
|
/// Used to chainify wrapper constructors.
|
||||||
|
fn wrap_with<U, F: FnOnce(Self) -> U>(self, f: F) -> U {
|
||||||
|
f(self)
|
||||||
|
}
|
||||||
|
|
||||||
/// Calls the given closure on `self`.
|
/// Calls the given closure on `self`.
|
||||||
fn with<F: FnOnce(&mut Self)>(mut self, f: F) -> Self {
|
fn with<F: FnOnce(&mut Self)>(mut self, f: F) -> Self {
|
||||||
f(&mut self);
|
f(&mut self);
|
||||||
|
@ -110,9 +110,9 @@ impl<T> XY<T> {
|
|||||||
///
|
///
|
||||||
/// assert_eq!(xy.run_if(cond, |v| v * 3), XY::new(Some(3), None));
|
/// assert_eq!(xy.run_if(cond, |v| v * 3), XY::new(Some(3), None));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn run_if<F, U>(self, condition: XY<bool>, f: F) -> XY<Option<U>>
|
pub fn run_if<F, U>(self, condition: XY<bool>, mut f: F) -> XY<Option<U>>
|
||||||
where
|
where
|
||||||
F: Fn(T) -> U,
|
F: FnMut(T) -> U,
|
||||||
{
|
{
|
||||||
self.zip_map(condition, |v, c| if c { Some(f(v)) } else { None })
|
self.zip_map(condition, |v, c| if c { Some(f(v)) } else { None })
|
||||||
}
|
}
|
||||||
@ -163,7 +163,7 @@ impl<T> XY<T> {
|
|||||||
(self.x, self.y)
|
(self.x, self.y)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a `XY` with references to this one's values.
|
/// Returns a `XY` with references to this one's values.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
@ -184,6 +184,11 @@ impl<T> XY<T> {
|
|||||||
XY::new(&self.x, &self.y)
|
XY::new(&self.x, &self.y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a `XY` with mutable references to this one's values.
|
||||||
|
pub fn as_ref_mut(&mut self) -> XY<&mut T> {
|
||||||
|
XY::new(&mut self.x, &mut self.y)
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates an iterator that returns references to `x`, then `y`.
|
/// Creates an iterator that returns references to `x`, then `y`.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
@ -331,9 +336,9 @@ impl<T> XY<T> {
|
|||||||
/// let xy = a.zip_map(b, |(a1, a2), b| if b { a1 } else { a2 });
|
/// let xy = a.zip_map(b, |(a1, a2), b| if b { a1 } else { a2 });
|
||||||
/// assert_eq!(xy, XY::new(1, 20));
|
/// assert_eq!(xy, XY::new(1, 20));
|
||||||
/// ```
|
/// ```
|
||||||
pub fn zip_map<U, V, F>(self, other: XY<U>, f: F) -> XY<V>
|
pub fn zip_map<U, V, F>(self, other: XY<U>, mut f: F) -> XY<V>
|
||||||
where
|
where
|
||||||
F: Fn(T, U) -> V,
|
F: FnMut(T, U) -> V,
|
||||||
{
|
{
|
||||||
XY::new(f(self.x, other.x), f(self.y, other.y))
|
XY::new(f(self.x, other.x), f(self.y, other.y))
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
use cursive::align::HAlign;
|
use cursive::event::{EventResult, Key};
|
||||||
use cursive::view::Scrollable;
|
use cursive::traits::With;
|
||||||
|
use cursive::view::{scroll::Scroller, Scrollable};
|
||||||
use cursive::views::{Dialog, Panel, TextView};
|
use cursive::views::{Dialog, Panel, TextView};
|
||||||
|
use cursive::{align::HAlign, views::OnEventView};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Read some long text from a file.
|
// Read some long text from a file.
|
||||||
@ -14,7 +16,29 @@ fn main() {
|
|||||||
// The text is too long to fit on a line, so the view will wrap lines,
|
// The text is too long to fit on a line, so the view will wrap lines,
|
||||||
// and will adapt to the terminal size.
|
// and will adapt to the terminal size.
|
||||||
siv.add_fullscreen_layer(
|
siv.add_fullscreen_layer(
|
||||||
Dialog::around(Panel::new(TextView::new(content).scrollable()))
|
Dialog::around(Panel::new(
|
||||||
|
TextView::new(content)
|
||||||
|
.scrollable()
|
||||||
|
.wrap_with(OnEventView::new)
|
||||||
|
.on_pre_event_inner(Key::PageUp, |v, _| {
|
||||||
|
let scroller = v.get_scroller_mut();
|
||||||
|
if scroller.can_scroll_up() {
|
||||||
|
scroller.scroll_up(
|
||||||
|
scroller.last_outer_size().y.saturating_sub(1),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Some(EventResult::Consumed(None))
|
||||||
|
})
|
||||||
|
.on_pre_event_inner(Key::PageDown, |v, _| {
|
||||||
|
let scroller = v.get_scroller_mut();
|
||||||
|
if scroller.can_scroll_down() {
|
||||||
|
scroller.scroll_down(
|
||||||
|
scroller.last_outer_size().y.saturating_sub(1),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Some(EventResult::Consumed(None))
|
||||||
|
}),
|
||||||
|
))
|
||||||
.title("Unicode and wide-character support")
|
.title("Unicode and wide-character support")
|
||||||
// This is the alignment for the button
|
// This is the alignment for the button
|
||||||
.h_align(HAlign::Center)
|
.h_align(HAlign::Center)
|
||||||
|
Loading…
Reference in New Issue
Block a user