Files
ratatrix/src/components/backlog.rs
2023-11-03 13:15:38 +01:00

114 lines
3.5 KiB
Rust

/*
* Copyright (C) 2023 Valentin Lorentz
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use super::Component;
use color_eyre::eyre::{Result, WrapErr};
use crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
use ratatui::{prelude::*, widgets::*};
use crate::components::Action;
use crate::widgets::{BottomAlignedParagraph, OverlappableWidget};
#[derive(Default)]
pub struct Backlog {
scroll: u64,
}
impl Component for Backlog {
fn handle_key_events(&mut self, key: KeyEvent) -> Result<Option<Action>> {
match key {
KeyEvent {
code: KeyCode::PageUp,
modifiers: KeyModifiers::NONE,
kind: KeyEventKind::Press,
state: _,
} => {
self.scroll += 20; // TODO: use the component height
},
KeyEvent {
code: KeyCode::PageDown,
modifiers: KeyModifiers::NONE,
kind: KeyEventKind::Press,
state: _,
} => {
self.scroll = self.scroll.saturating_sub(20); // TODO: use the component height
},
_ => {},
}
Ok(None)
}
fn draw(
&mut self,
frame: &mut Frame<'_>,
area: Rect,
buffers: &crate::buffers::Buffers,
) -> Result<()> {
let block = Block::new().borders(Borders::ALL);
let mut text_area = block.inner(area);
block.render(area, frame.buffer_mut());
let active_buffer = buffers.active_buffer();
let mut items = active_buffer.content();
items.reverse();
let mut items = items.into_iter();
let mut scroll = self.scroll;
// Skip widgets at the bottom (if scrolled up), and render the first visible one
loop {
let Some(item) = items.next() else {
break;
};
let widget = BottomAlignedParagraph::new(item);
let expected_height = widget.height(text_area.width);
if scroll.saturating_sub(expected_height) > text_area.height.into() {
// Paragraph is too far down, not displayed
scroll -= expected_height;
continue;
}
let widget = widget.scroll(scroll);
let height = widget.render_overlap(text_area, frame.buffer_mut());
text_area.height = text_area.height.saturating_sub(height);
scroll = scroll.saturating_sub(expected_height);
}
if text_area.height == 0 {
// No more room to display other paragraphs, stop now
return Ok(());
}
// Render other widgets
for item in items {
let widget = BottomAlignedParagraph::new(item);
let height = widget.render_overlap(text_area, frame.buffer_mut());
assert!(area.height >= height, "{:?} {}", area, height);
text_area.height = text_area.height.saturating_sub(height); // Remove lines at the bottom used by this paragraph
if text_area.height == 0 {
// No more room to display other paragraphs, stop now
return Ok(());
}
}
// There is empty room on screen, ask the buffer to fetch more backlog if it can
active_buffer.request_back_pagination(100);
Ok(())
}
}