feat(tui): support scrolling the kernel documentation

This commit is contained in:
Orhun Parmaksız 2022-01-22 00:04:30 +03:00
parent afb9b9430e
commit 9a61d9fbc8
No known key found for this signature in database
GPG key ID: F83424824B3E4B90
4 changed files with 64 additions and 21 deletions

View file

@ -1,7 +1,6 @@
use crate::command::Command;
use crate::error::Result;
use crate::options::CopyOption;
use crate::options::Direction;
use crate::options::{CopyOption, Direction, ScrollArea};
use crate::widgets::StatefulTable;
#[cfg(feature = "clipboard")]
use copypasta_ext::{display::DisplayServer, prelude::ClipboardProvider};
@ -26,6 +25,8 @@ pub struct App<'a> {
pub input_cursor: u16,
/// Whether if the search mode is enabled.
pub search_mode: bool,
/// Y-scroll offset for the documentation.
pub docs_scroll_amount: u16,
/// Entries of the options menu.
pub options: Option<StatefulTable<&'a str>>,
/// List of sysctl parameters.
@ -46,6 +47,7 @@ impl<'a> App<'a> {
input_time: None,
input_cursor: 0,
search_mode: false,
docs_scroll_amount: 0,
options: None,
parameter_list: StatefulTable::default(),
#[cfg(feature = "clipboard")]
@ -92,6 +94,7 @@ impl<'a> App<'a> {
} else {
self.parameter_list = StatefulTable::with_items(self.sysctl.parameters.clone());
}
self.docs_scroll_amount = 0;
}
/// Copies the selected entry to the clipboard.
@ -165,10 +168,11 @@ impl<'a> App<'a> {
self.input_time = Some(Instant::now());
}
}
Command::Scroll(Direction::Up, amount) => {
Command::Scroll(ScrollArea::List, Direction::Up, amount) => {
if let Some(options) = self.options.as_mut() {
options.previous();
} else if !self.parameter_list.items.is_empty() {
self.docs_scroll_amount = 0;
if amount == 1 {
self.parameter_list.previous();
} else {
@ -182,10 +186,11 @@ impl<'a> App<'a> {
}
}
}
Command::Scroll(Direction::Down, amount) => {
Command::Scroll(ScrollArea::List, Direction::Down, amount) => {
if let Some(options) = self.options.as_mut() {
options.next();
} else if !self.parameter_list.items.is_empty() {
self.docs_scroll_amount = 0;
if amount == 1 {
self.parameter_list.next();
} else {
@ -204,16 +209,31 @@ impl<'a> App<'a> {
}
}
}
Command::Scroll(Direction::Top, _) => {
Command::Scroll(ScrollArea::List, Direction::Top, _) => {
if !self.parameter_list.items.is_empty() {
self.docs_scroll_amount = 0;
self.parameter_list.state.select(Some(0));
}
}
Command::Scroll(Direction::Bottom, _) => {
Command::Scroll(ScrollArea::List, Direction::Bottom, _) => {
if let Some(last_index) = self.parameter_list.items.len().checked_sub(1) {
self.docs_scroll_amount = 0;
self.parameter_list.state.select(Some(last_index))
}
}
Command::Scroll(ScrollArea::Documentation, Direction::Up, amount) => {
self.docs_scroll_amount = self
.docs_scroll_amount
.checked_sub(amount.into())
.unwrap_or_default();
}
Command::Scroll(ScrollArea::Documentation, Direction::Down, amount) => {
self.docs_scroll_amount = self
.docs_scroll_amount
.checked_add(amount.into())
.unwrap_or(self.docs_scroll_amount);
}
Command::Scroll(_, _, _) => {}
Command::EnableSearch => {
if self.input_time.is_some() {
self.input_time = None;
@ -312,6 +332,7 @@ impl<'a> App<'a> {
Command::Refresh => {
self.input = None;
self.sysctl.parameters = Sysctl::init(self.sysctl.config.clone())?.parameters;
self.docs_scroll_amount = 0;
self.parameter_list.items.iter_mut().for_each(|parameter| {
if let Some(param) = self
.sysctl

View file

@ -1,4 +1,4 @@
use crate::options::Direction;
use crate::options::{Direction, ScrollArea};
use std::str::FromStr;
use termion::event::Key;
@ -10,7 +10,7 @@ pub enum Command {
/// Set the value of a parameter.
Set(String, String),
/// Scroll the widget.
Scroll(Direction, u8),
Scroll(ScrollArea, Direction, u8),
/// Move cursor to right/left.
MoveCursor(u8),
/// Enable the search mode.
@ -48,13 +48,10 @@ impl FromStr for Command {
values.next().ok_or(())?.to_string(),
))
} else if s.starts_with("scroll") {
let mut values = s.trim_start_matches("scroll").trim().split_whitespace();
Ok(Command::Scroll(
Direction::try_from(
*(s.split_whitespace()
.collect::<Vec<&str>>()
.get(1)
.ok_or(())?),
)?,
ScrollArea::try_from(values.next().ok_or(())?)?,
Direction::try_from(values.next().ok_or(())?)?,
1,
))
} else {
@ -81,12 +78,14 @@ impl Command {
}
} else {
match key {
Key::Up => Command::Scroll(Direction::Up, 1),
Key::Down => Command::Scroll(Direction::Down, 1),
Key::PageUp => Command::Scroll(Direction::Up, 4),
Key::PageDown => Command::Scroll(Direction::Down, 4),
Key::Char('t') => Command::Scroll(Direction::Top, 0),
Key::Char('b') => Command::Scroll(Direction::Bottom, 0),
Key::Up => Command::Scroll(ScrollArea::List, Direction::Up, 1),
Key::Down => Command::Scroll(ScrollArea::List, Direction::Down, 1),
Key::PageUp => Command::Scroll(ScrollArea::List, Direction::Up, 4),
Key::PageDown => Command::Scroll(ScrollArea::List, Direction::Down, 4),
Key::Char('t') => Command::Scroll(ScrollArea::List, Direction::Top, 0),
Key::Char('b') => Command::Scroll(ScrollArea::List, Direction::Bottom, 0),
Key::Left => Command::Scroll(ScrollArea::Documentation, Direction::Up, 1),
Key::Right => Command::Scroll(ScrollArea::Documentation, Direction::Down, 1),
Key::Char(':') => Command::UpdateInput(' '),
Key::Char('/') => Command::EnableSearch,
Key::Char('\n') => Command::Select,

View file

@ -54,3 +54,9 @@ generate_option!(
Top => "top",
Bottom => "bottom",
);
generate_option!(
ScrollArea,
List => "list",
Documentation => "documentation",
);

View file

@ -38,7 +38,12 @@ pub fn render<B: Backend>(frame: &mut Frame<'_, B>, app: &mut App) {
}
}
if let Some(documentation) = documentation {
render_parameter_documentation(frame, chunks[1], documentation);
render_parameter_documentation(
frame,
chunks[1],
documentation,
&mut app.docs_scroll_amount,
);
}
}
@ -221,7 +226,18 @@ fn render_parameter_documentation<B: Backend>(
frame: &mut Frame<'_, B>,
rect: Rect,
documentation: String,
scroll_amount: &mut u16,
) {
match (documentation.lines().count() * 2).checked_sub(rect.height.into()) {
Some(scroll_overflow) => {
if scroll_overflow < (*scroll_amount).into() {
*scroll_amount = scroll_overflow as u16;
}
}
None => {
*scroll_amount = scroll_amount.checked_sub(1).unwrap_or_default();
}
}
frame.render_widget(
Paragraph::new(documentation)
.block(
@ -236,6 +252,7 @@ fn render_parameter_documentation<B: Backend>(
.border_type(BorderType::Rounded)
.style(Style::default().bg(Color::Black)),
)
.scroll((*scroll_amount, 0))
.wrap(Wrap { trim: false }),
rect,
);