use rat_event::util::MouseFlags;
use rat_event::{ConsumedEvent, HandleEvent, MouseOnly, Outcome, Regular, ct_event};
use rat_focus::{FocusBuilder, FocusFlag, HasFocus};
use rat_reloc::{RelocatableState, relocate_area};
use rat_scrolled::event::ScrollOutcome;
use rat_scrolled::{Scroll, ScrollArea, ScrollAreaState, ScrollState};
use rat_text::line_number::{LineNumberState, LineNumbers};
use rat_widget::util::revert_style;
use ratatui_core::buffer::Buffer;
use ratatui_core::layout::Rect;
use ratatui_core::style::Style;
use ratatui_core::widgets::StatefulWidget;
use ratatui_crossterm::crossterm::event::Event;
use ratatui_widgets::block::Block;
#[derive(Debug, Default)]
pub struct EndlessScroll<'a> {
style: Style,
focus_style: Option<Style>,
cursor_style: Option<Style>,
v_scroll: Option<Scroll<'a>>,
block: Option<Block<'a>>,
max: usize,
}
#[derive(Debug, Default)]
pub struct EndlessScrollState {
pub area: Rect,
pub widget_area: Rect,
pub line_num: LineNumberState,
pub vscroll: ScrollState,
pub focus: FocusFlag,
pub mouse: MouseFlags,
}
impl<'a> EndlessScroll<'a> {
pub fn new() -> Self {
Self {
style: Default::default(),
focus_style: None,
cursor_style: None,
v_scroll: None,
block: None,
max: 0,
}
}
pub fn max(mut self, max: usize) -> Self {
self.max = max;
self
}
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self.block = self.block.map(|v| v.style(style));
self
}
pub fn focus_style(mut self, style: impl Into<Style>) -> Self {
self.focus_style = Some(style.into());
self
}
pub fn cursor_style(mut self, style: Style) -> Self {
self.cursor_style = Some(style);
self
}
pub fn v_scroll(mut self, scroll: Scroll<'a>) -> Self {
self.v_scroll = Some(scroll);
self
}
pub fn block(mut self, block: Block<'a>) -> Self {
self.block = Some(block.style(self.style));
self
}
}
impl<'a> StatefulWidget for EndlessScroll<'a> {
type State = EndlessScrollState;
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
state.area = area;
let sa = ScrollArea::new()
.block(self.block.as_ref())
.v_scroll(self.v_scroll.as_ref());
state.widget_area = sa.inner(area, None, Some(&state.vscroll));
state
.vscroll
.set_max_offset(self.max.saturating_sub(area.height as usize));
state.vscroll.set_page_len(area.height as usize);
if self.block.is_none() {
buf.set_style(area, self.style);
}
sa.render(
area,
buf,
&mut ScrollAreaState::new().v_scroll(&mut state.vscroll),
);
let focus_style = self.focus_style.unwrap_or(revert_style(self.style));
let cursor_style = if state.is_focused() {
focus_style
} else {
self.cursor_style.unwrap_or(self.style)
};
LineNumbers::new()
.start(state.vscroll.offset as u32)
.cursor(state.vscroll.offset as u32 + area.height as u32 / 2)
.cursor_style(cursor_style)
.render(state.widget_area, buf, &mut state.line_num);
}
}
impl HasFocus for EndlessScrollState {
fn build(&self, builder: &mut FocusBuilder) {
builder.leaf_widget(self);
}
fn focus(&self) -> FocusFlag {
self.focus.clone()
}
fn area(&self) -> Rect {
self.area
}
}
impl RelocatableState for EndlessScrollState {
fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
self.area = relocate_area(self.area, shift, clip);
self.widget_area = relocate_area(self.widget_area, shift, clip);
}
}
impl EndlessScrollState {
pub fn new() -> Self {
Self::default()
}
}
impl HandleEvent<Event, Regular, Outcome> for EndlessScrollState {
fn handle(&mut self, event: &Event, _qualifier: Regular) -> Outcome {
let r = if self.is_focused() {
match event {
ct_event!(keycode press Up) => self.vscroll.scroll_up(1).into(),
ct_event!(keycode press Down) => self.vscroll.scroll_down(1).into(),
ct_event!(keycode press PageUp) => {
self.vscroll.scroll_up(self.vscroll.page_len / 2).into()
}
ct_event!(keycode press PageDown) => {
self.vscroll.scroll_down(self.vscroll.page_len / 2).into()
}
ct_event!(keycode press Home) => self.vscroll.scroll_to_pos(0).into(),
ct_event!(keycode press End) => {
self.vscroll.scroll_to_pos(self.vscroll.max_offset).into()
}
_ => Outcome::Continue,
}
} else {
Outcome::Continue
};
if !r.is_consumed() {
self.handle(event, MouseOnly)
} else {
r
}
}
}
impl HandleEvent<Event, MouseOnly, Outcome> for EndlessScrollState {
fn handle(&mut self, event: &Event, _qualifier: MouseOnly) -> Outcome {
match ScrollAreaState::new()
.area(self.area)
.v_scroll(&mut self.vscroll)
.handle(event, MouseOnly)
{
ScrollOutcome::Up(n) => self.vscroll.scroll_up(n).into(),
ScrollOutcome::Down(n) => self.vscroll.scroll_down(n).into(),
ScrollOutcome::VPos(p) => self.vscroll.set_offset(p).into(),
r => r.into(),
}
}
}