mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 09:25:01 +00:00
Add new ScrollBase
This commit is contained in:
parent
bc7972d539
commit
ae0e9216ce
@ -29,6 +29,9 @@ pub struct Printer<'a, 'b> {
|
||||
/// Size of the area we are allowed to draw on.
|
||||
///
|
||||
/// Anything outside of this should be discarded.
|
||||
///
|
||||
/// The view being drawn can ingore this, but anything further than that
|
||||
/// will be ignored.
|
||||
pub output_size: Vec2,
|
||||
|
||||
/// Size allocated to the view.
|
||||
@ -39,6 +42,9 @@ pub struct Printer<'a, 'b> {
|
||||
|
||||
/// Offset into the view for this printer.
|
||||
///
|
||||
/// The view being drawn can ignore this, but anything to the top-left of
|
||||
/// this will actually be ignored, so it can be used to skip this part.
|
||||
///
|
||||
/// A print request `x`, will really print at `x - content_offset`.
|
||||
pub content_offset: Vec2,
|
||||
|
||||
|
144
src/view/scroll/base.rs
Normal file
144
src/view/scroll/base.rs
Normal file
@ -0,0 +1,144 @@
|
||||
use crate::vec::Vec2;
|
||||
use crate::Printer;
|
||||
|
||||
use crate::direction::Direction;
|
||||
use crate::event::{Event, EventResult};
|
||||
use crate::view::scroll::ScrollCore;
|
||||
use crate::view::scroll::{InnerLayout, InnerOnEvent, InnerRequiredSize};
|
||||
|
||||
/// Provide scrolling functionalities to a view.
|
||||
///
|
||||
/// You're not supposed to use this directly,
|
||||
/// but it can be helpful if you create your own Views.
|
||||
#[derive(Default, Debug)]
|
||||
pub struct ScrollBase {
|
||||
core: ScrollCore,
|
||||
|
||||
/// Number of lines displayed
|
||||
pub view_height: usize,
|
||||
|
||||
/// Blank between the text and the scrollbar.
|
||||
pub right_padding: usize,
|
||||
|
||||
/// Initial position of the cursor when dragging.
|
||||
pub thumb_grab: Option<usize>,
|
||||
}
|
||||
|
||||
struct RequiredSize<F>(F);
|
||||
|
||||
impl<F> InnerRequiredSize for RequiredSize<F>
|
||||
where
|
||||
F: FnMut(Vec2) -> Vec2,
|
||||
{
|
||||
fn needs_relayout(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn required_size(&mut self, constraint: Vec2) -> Vec2 {
|
||||
self.0(constraint)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> InnerLayout for RequiredSize<F>
|
||||
where
|
||||
F: FnMut(Vec2) -> Vec2,
|
||||
{
|
||||
fn layout(&mut self, size: Vec2) {}
|
||||
|
||||
fn needs_relayout(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn required_size(&mut self, constraint: Vec2) -> Vec2 {
|
||||
self.0(constraint)
|
||||
}
|
||||
}
|
||||
|
||||
impl ScrollBase {
|
||||
/// Creates a new, uninitialized scrollbar.
|
||||
pub fn new() -> Self {
|
||||
ScrollBase {
|
||||
core: ScrollCore::new(),
|
||||
|
||||
view_height: 0,
|
||||
right_padding: 1,
|
||||
thumb_grab: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs `View::layout()`.
|
||||
pub fn layout<F>(&mut self, size: Vec2, required_size: F)
|
||||
where
|
||||
F: FnMut(Vec2) -> Vec2,
|
||||
{
|
||||
self.core.layout(size, RequiredSize(required_size));
|
||||
}
|
||||
|
||||
/// Performs `View::required_size()`.
|
||||
pub fn required_size<F>(
|
||||
&mut self, constraint: Vec2, required_size: F,
|
||||
) -> Vec2
|
||||
where
|
||||
F: FnMut(Vec2) -> Vec2,
|
||||
{
|
||||
self.core
|
||||
.required_size(constraint, RequiredSize(required_size))
|
||||
}
|
||||
|
||||
/// Draws the scroll bar and the content using the given drawer.
|
||||
///
|
||||
/// `line_drawer` will be called once for each line that needs to be drawn.
|
||||
/// It will be given the absolute ID of the item to draw..
|
||||
/// It will also be given a printer with the correct offset,
|
||||
/// so it should only print on the first line.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use cursive::view::ScrollBase;
|
||||
/// # use cursive::Printer;
|
||||
/// # use cursive::theme;
|
||||
/// # use cursive::backend;
|
||||
/// # let scrollbase = ScrollBase::new();
|
||||
/// # let b = backend::dummy::Backend::init();
|
||||
/// # let t = theme::load_default();
|
||||
/// # let printer = Printer::new((5,1), &t, &*b);
|
||||
/// # let printer = &printer;
|
||||
/// let lines = ["Line 1", "Line number 2"];
|
||||
/// scrollbase.draw(printer, |printer, i| {
|
||||
/// printer.print((0,0), lines[i]);
|
||||
/// });
|
||||
/// ```
|
||||
pub fn draw<F>(&self, printer: &Printer<'_, '_>, mut line_drawer: F)
|
||||
where
|
||||
F: FnMut(&Printer<'_, '_>, usize),
|
||||
{
|
||||
self.core.draw(printer, |printer| {
|
||||
let start = printer.content_offset.y;
|
||||
let end = start + printer.output_size.y;
|
||||
for y in start..end {
|
||||
let printer =
|
||||
printer.offset((y, 0)).cropped((printer.size.x, 1));
|
||||
line_drawer(&printer, y);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Performs `View::take_focus()`.
|
||||
pub fn take_focus<F>(
|
||||
&mut self, source: Direction, inner_take_focus: F,
|
||||
) -> bool
|
||||
where
|
||||
F: FnOnce(Direction) -> bool,
|
||||
{
|
||||
self.core.take_focus(source, inner_take_focus)
|
||||
}
|
||||
|
||||
/// Performs `View::on_event()`.
|
||||
pub fn on_event<I>(&mut self, event: Event, inner: I) -> EventResult
|
||||
where
|
||||
I: InnerOnEvent,
|
||||
{
|
||||
self.core.on_event(event, inner)
|
||||
}
|
||||
}
|
@ -10,13 +10,12 @@ use crate::view::{ScrollStrategy, Selector, SizeCache};
|
||||
use crate::with::With;
|
||||
use crate::XY;
|
||||
|
||||
use crate::view::scroll::{
|
||||
InnerDraw, InnerLayout, InnerOnEvent, InnerRequiredSize,
|
||||
};
|
||||
use crate::view::scroll::{InnerLayout, InnerOnEvent, InnerRequiredSize};
|
||||
|
||||
/// Core system for scrolling views.
|
||||
///
|
||||
/// See also [`ScrollView`](crate::views::ScrollView).
|
||||
#[derive(Debug)]
|
||||
pub struct ScrollCore {
|
||||
/// This is the size the child thinks we're giving him.
|
||||
inner_size: Vec2,
|
||||
@ -81,7 +80,10 @@ impl ScrollCore {
|
||||
}
|
||||
|
||||
/// Performs the `View::draw()` operation.
|
||||
pub fn draw<I: InnerDraw>(&self, printer: &Printer<'_, '_>, inner: I) {
|
||||
pub fn draw<F>(&self, printer: &Printer<'_, '_>, inner_draw: F)
|
||||
where
|
||||
F: FnOnce(&Printer<'_, '_>),
|
||||
{
|
||||
// Draw scrollbar?
|
||||
let scrolling = self.is_scrolling();
|
||||
|
||||
@ -141,7 +143,7 @@ impl ScrollCore {
|
||||
.content_offset(self.offset)
|
||||
.inner_size(self.inner_size);
|
||||
|
||||
inner.draw(&printer);
|
||||
inner_draw(&printer);
|
||||
}
|
||||
|
||||
/// Performs `View::on_event()`
|
||||
@ -180,10 +182,12 @@ impl ScrollCore {
|
||||
|
||||
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,
|
||||
// but not the selection.
|
||||
// without affecting the selection.
|
||||
match event {
|
||||
Event::Mouse {
|
||||
event: MouseEvent::WheelUp,
|
||||
@ -287,6 +291,8 @@ impl ScrollCore {
|
||||
EventResult::Consumed(None)
|
||||
}
|
||||
other => {
|
||||
// The view consumed the event. Maybe something changed?
|
||||
|
||||
// Fix offset?
|
||||
let important = inner.important_area(self.inner_size);
|
||||
|
||||
@ -409,6 +415,22 @@ impl ScrollCore {
|
||||
self.with(|s| s.set_scroll_strategy(strategy))
|
||||
}
|
||||
|
||||
/// Sets the padding between content and scrollbar.
|
||||
pub fn set_scrollbar_padding<V: Into<Vec2>>(
|
||||
&mut self, scrollbar_padding: V,
|
||||
) {
|
||||
self.scrollbar_padding = scrollbar_padding.into();
|
||||
}
|
||||
|
||||
/// Sets the padding between content and scrollbar.
|
||||
///
|
||||
/// Chainable variant.
|
||||
pub fn scrollbar_padding<V: Into<Vec2>>(
|
||||
self, scrollbar_padding: V,
|
||||
) -> Self {
|
||||
self.with(|s| s.set_scrollbar_padding(scrollbar_padding))
|
||||
}
|
||||
|
||||
/// Control whether scroll bars are visibile.
|
||||
///
|
||||
/// Defaults to `true`.
|
||||
@ -466,6 +488,15 @@ impl ScrollCore {
|
||||
self.with(|s| s.set_scroll_x(enabled))
|
||||
}
|
||||
|
||||
/// Scroll until the given column is visible.
|
||||
pub fn scroll_to_x(&mut self, x: usize) {
|
||||
if x > self.offset.x + self.last_size.x {
|
||||
self.offset.x = 1 + x - self.last_size.x;
|
||||
} else if x < self.offset.x {
|
||||
self.offset.x = x;
|
||||
}
|
||||
}
|
||||
|
||||
/// Programmatically scroll to the top of the view.
|
||||
pub fn scroll_to_top(&mut self) {
|
||||
let curr_x = self.offset.x;
|
||||
|
@ -4,13 +4,13 @@
|
||||
//!
|
||||
//! [`ScrollView`](crate::views::ScrollView) may be an easier way to add scrolling to an existing view.
|
||||
|
||||
mod base;
|
||||
mod core;
|
||||
mod traits;
|
||||
|
||||
pub use self::base::ScrollBase;
|
||||
pub use self::core::ScrollCore;
|
||||
pub use self::traits::{
|
||||
InnerDraw, InnerLayout, InnerOnEvent, InnerRequiredSize,
|
||||
};
|
||||
pub use self::traits::{InnerLayout, InnerOnEvent, InnerRequiredSize};
|
||||
|
||||
/// Defines the scrolling behaviour on content or size change
|
||||
#[derive(Debug)]
|
||||
|
@ -1,5 +1,4 @@
|
||||
use crate::event::{Event, EventResult};
|
||||
use crate::printer::Printer;
|
||||
use crate::rect::Rect;
|
||||
|
||||
use crate::vec::Vec2;
|
||||
@ -24,17 +23,6 @@ impl<'a, V: View> InnerOnEvent for &'a mut V {
|
||||
}
|
||||
|
||||
/// Inner implementation for `ScrollCore::draw()`
|
||||
pub trait InnerDraw {
|
||||
/// Performs `View::draw()`
|
||||
fn draw(&self, printer: &Printer<'_, '_>);
|
||||
}
|
||||
|
||||
impl<'a, V: View> InnerDraw for &'a V {
|
||||
fn draw(&self, printer: &Printer<'_, '_>) {
|
||||
<V as View>::draw(self, printer);
|
||||
}
|
||||
}
|
||||
|
||||
/// Inner implementation for `ScrollCore::InnerLayout()`
|
||||
pub trait InnerLayout {
|
||||
/// Performs `View::layout()`
|
||||
|
Loading…
Reference in New Issue
Block a user