mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +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.
|
/// Size of the area we are allowed to draw on.
|
||||||
///
|
///
|
||||||
/// Anything outside of this should be discarded.
|
/// 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,
|
pub output_size: Vec2,
|
||||||
|
|
||||||
/// Size allocated to the view.
|
/// Size allocated to the view.
|
||||||
@ -39,6 +42,9 @@ pub struct Printer<'a, 'b> {
|
|||||||
|
|
||||||
/// Offset into the view for this printer.
|
/// 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`.
|
/// A print request `x`, will really print at `x - content_offset`.
|
||||||
pub content_offset: Vec2,
|
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::with::With;
|
||||||
use crate::XY;
|
use crate::XY;
|
||||||
|
|
||||||
use crate::view::scroll::{
|
use crate::view::scroll::{InnerLayout, InnerOnEvent, InnerRequiredSize};
|
||||||
InnerDraw, InnerLayout, InnerOnEvent, InnerRequiredSize,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Core system for scrolling views.
|
/// Core system for scrolling views.
|
||||||
///
|
///
|
||||||
/// See also [`ScrollView`](crate::views::ScrollView).
|
/// See also [`ScrollView`](crate::views::ScrollView).
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ScrollCore {
|
pub struct ScrollCore {
|
||||||
/// This is the size the child thinks we're giving him.
|
/// This is the size the child thinks we're giving him.
|
||||||
inner_size: Vec2,
|
inner_size: Vec2,
|
||||||
@ -81,7 +80,10 @@ impl ScrollCore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Performs the `View::draw()` operation.
|
/// 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?
|
// Draw scrollbar?
|
||||||
let scrolling = self.is_scrolling();
|
let scrolling = self.is_scrolling();
|
||||||
|
|
||||||
@ -141,7 +143,7 @@ impl ScrollCore {
|
|||||||
.content_offset(self.offset)
|
.content_offset(self.offset)
|
||||||
.inner_size(self.inner_size);
|
.inner_size(self.inner_size);
|
||||||
|
|
||||||
inner.draw(&printer);
|
inner_draw(&printer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs `View::on_event()`
|
/// Performs `View::on_event()`
|
||||||
@ -180,10 +182,12 @@ impl ScrollCore {
|
|||||||
|
|
||||||
match result {
|
match result {
|
||||||
EventResult::Ignored => {
|
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 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,
|
// Also allow Ctrl+arrow to move the view,
|
||||||
// but not the selection.
|
// without affecting the selection.
|
||||||
match event {
|
match event {
|
||||||
Event::Mouse {
|
Event::Mouse {
|
||||||
event: MouseEvent::WheelUp,
|
event: MouseEvent::WheelUp,
|
||||||
@ -287,6 +291,8 @@ impl ScrollCore {
|
|||||||
EventResult::Consumed(None)
|
EventResult::Consumed(None)
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
|
// The view consumed the event. Maybe something changed?
|
||||||
|
|
||||||
// Fix offset?
|
// Fix offset?
|
||||||
let important = inner.important_area(self.inner_size);
|
let important = inner.important_area(self.inner_size);
|
||||||
|
|
||||||
@ -409,6 +415,22 @@ impl ScrollCore {
|
|||||||
self.with(|s| s.set_scroll_strategy(strategy))
|
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.
|
/// Control whether scroll bars are visibile.
|
||||||
///
|
///
|
||||||
/// Defaults to `true`.
|
/// Defaults to `true`.
|
||||||
@ -466,6 +488,15 @@ impl ScrollCore {
|
|||||||
self.with(|s| s.set_scroll_x(enabled))
|
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.
|
/// 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;
|
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.
|
//! [`ScrollView`](crate::views::ScrollView) may be an easier way to add scrolling to an existing view.
|
||||||
|
|
||||||
|
mod base;
|
||||||
mod core;
|
mod core;
|
||||||
mod traits;
|
mod traits;
|
||||||
|
|
||||||
|
pub use self::base::ScrollBase;
|
||||||
pub use self::core::ScrollCore;
|
pub use self::core::ScrollCore;
|
||||||
pub use self::traits::{
|
pub use self::traits::{InnerLayout, InnerOnEvent, InnerRequiredSize};
|
||||||
InnerDraw, InnerLayout, InnerOnEvent, InnerRequiredSize,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Defines the scrolling behaviour on content or size change
|
/// Defines the scrolling behaviour on content or size change
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use crate::event::{Event, EventResult};
|
use crate::event::{Event, EventResult};
|
||||||
use crate::printer::Printer;
|
|
||||||
use crate::rect::Rect;
|
use crate::rect::Rect;
|
||||||
|
|
||||||
use crate::vec::Vec2;
|
use crate::vec::Vec2;
|
||||||
@ -24,17 +23,6 @@ impl<'a, V: View> InnerOnEvent for &'a mut V {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Inner implementation for `ScrollCore::draw()`
|
/// 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()`
|
/// Inner implementation for `ScrollCore::InnerLayout()`
|
||||||
pub trait InnerLayout {
|
pub trait InnerLayout {
|
||||||
/// Performs `View::layout()`
|
/// Performs `View::layout()`
|
||||||
|
Loading…
Reference in New Issue
Block a user