mirror of
https://codeberg.org/icewind/logsmash.git
synced 2026-06-03 18:14:11 +02:00
scrollbartable widget
This commit is contained in:
parent
8f310cadcf
commit
5d4a17f289
7 changed files with 157 additions and 178 deletions
|
|
@ -1,13 +1,14 @@
|
||||||
use crate::app::{App, LogMatch};
|
use crate::app::{App, LogMatch};
|
||||||
use crate::ui::histogram::sparkline;
|
use crate::ui::histogram::sparkline;
|
||||||
use crate::ui::style::{TABLE_HEADER_STYLE, TABLE_SELECTED_STYLE};
|
use crate::ui::style::TABLE_HEADER_STYLE;
|
||||||
|
use crate::ui::table::ScrollbarTable;
|
||||||
use itertools::Either;
|
use itertools::Either;
|
||||||
use ratatui::prelude::*;
|
use ratatui::prelude::*;
|
||||||
use ratatui::widgets::{Cell, HighlightSpacing, Row, Table};
|
use ratatui::widgets::{Cell, Row};
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
use std::iter::{empty, once};
|
use std::iter::{empty, once};
|
||||||
|
|
||||||
pub fn match_list(app: &App) -> Table {
|
pub fn match_list(app: &App) -> ScrollbarTable {
|
||||||
let header = [
|
let header = [
|
||||||
Text::from("Statement"),
|
Text::from("Statement"),
|
||||||
Text::from("File"),
|
Text::from("File"),
|
||||||
|
|
@ -36,15 +37,13 @@ pub fn match_list(app: &App) -> Table {
|
||||||
Either::Left(once(log_row(&app.unmatched, app, "Unmatched lines")))
|
Either::Left(once(log_row(&app.unmatched, app, "Unmatched lines")))
|
||||||
};
|
};
|
||||||
|
|
||||||
Table::new(
|
ScrollbarTable::new(
|
||||||
once(all)
|
once(all)
|
||||||
.chain(app.matches.iter().map(|result| log_row(result, app, "")))
|
.chain(app.matches.iter().map(|result| log_row(result, app, "")))
|
||||||
.chain(unmatched),
|
.chain(unmatched),
|
||||||
widths,
|
widths,
|
||||||
)
|
)
|
||||||
.header(header)
|
.header(header)
|
||||||
.highlight_style(TABLE_SELECTED_STYLE)
|
|
||||||
.highlight_spacing(HighlightSpacing::Always)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn log_row<'a>(result: &LogMatch, app: &'a App, name: &'static str) -> Row<'a> {
|
fn log_row<'a>(result: &LogMatch, app: &'a App, name: &'static str) -> Row<'a> {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ use ratatui::crossterm::terminal::{
|
||||||
};
|
};
|
||||||
use ratatui::crossterm::{event, ExecutableCommand};
|
use ratatui::crossterm::{event, ExecutableCommand};
|
||||||
use ratatui::prelude::*;
|
use ratatui::prelude::*;
|
||||||
use ratatui::widgets::{Block, Borders, Scrollbar, ScrollbarOrientation};
|
|
||||||
use ratatui::Terminal;
|
use ratatui::Terminal;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::io::stdout;
|
use std::io::stdout;
|
||||||
|
|
@ -26,6 +25,7 @@ mod single_log;
|
||||||
mod single_match;
|
mod single_match;
|
||||||
mod state;
|
mod state;
|
||||||
pub mod style;
|
pub mod style;
|
||||||
|
mod table;
|
||||||
|
|
||||||
pub fn run_ui(app: App) -> Result<(), UiError> {
|
pub fn run_ui(app: App) -> Result<(), UiError> {
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
|
|
@ -91,11 +91,8 @@ fn ui(frame: &mut Frame, app: &App, state: &mut UiState) {
|
||||||
|
|
||||||
match state {
|
match state {
|
||||||
UiState::Quit => {}
|
UiState::Quit => {}
|
||||||
UiState::MatchList(MatchListState {
|
UiState::MatchList(MatchListState { table_state }) => {
|
||||||
table_state,
|
let selected = table_state.selected();
|
||||||
scroll_state,
|
|
||||||
}) => {
|
|
||||||
let selected = table_state.selected().unwrap_or(0);
|
|
||||||
let histogram = if selected == 0 {
|
let histogram = if selected == 0 {
|
||||||
&app.all.histogram
|
&app.all.histogram
|
||||||
} else if selected < app.matches.len() + 1 {
|
} else if selected < app.matches.len() + 1 {
|
||||||
|
|
@ -104,75 +101,30 @@ fn ui(frame: &mut Frame, app: &App, state: &mut UiState) {
|
||||||
} else {
|
} else {
|
||||||
&app.unmatched.histogram
|
&app.unmatched.histogram
|
||||||
};
|
};
|
||||||
let scrollbar = Scrollbar::new(ScrollbarOrientation::VerticalRight)
|
|
||||||
.begin_symbol(Some("↑"))
|
|
||||||
.end_symbol(Some("↓"));
|
|
||||||
|
|
||||||
frame.render_widget(UiHistogram::new(histogram), layout[0]);
|
frame.render_widget(UiHistogram::new(histogram), layout[0]);
|
||||||
frame.render_stateful_widget(
|
frame.render_stateful_widget(match_list(app), layout[1], table_state);
|
||||||
match_list(app).block(Block::new().borders(Borders::RIGHT)),
|
|
||||||
layout[1],
|
|
||||||
table_state,
|
|
||||||
);
|
|
||||||
frame.render_stateful_widget(
|
|
||||||
scrollbar,
|
|
||||||
layout[1].inner(Margin {
|
|
||||||
vertical: 1,
|
|
||||||
horizontal: 0,
|
|
||||||
}),
|
|
||||||
scroll_state,
|
|
||||||
);
|
|
||||||
frame.render_widget(footer(app, page), layout[2]);
|
frame.render_widget(footer(app, page), layout[2]);
|
||||||
}
|
}
|
||||||
UiState::Match(MatchState {
|
UiState::Match(MatchState {
|
||||||
result,
|
result,
|
||||||
table_state,
|
table_state,
|
||||||
scroll_state,
|
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
let selected_group = &result.grouped[table_state.selected().unwrap_or_default()];
|
let selected_group = &result.grouped[table_state.selected()];
|
||||||
let scrollbar = Scrollbar::new(ScrollbarOrientation::VerticalRight)
|
|
||||||
.begin_symbol(Some("↑"))
|
|
||||||
.end_symbol(Some("↓"));
|
|
||||||
|
|
||||||
frame.render_widget(UiHistogram::new(&selected_group.histogram), layout[0]);
|
frame.render_widget(UiHistogram::new(&selected_group.histogram), layout[0]);
|
||||||
frame.render_stateful_widget(
|
frame.render_stateful_widget(grouped_lines(app, result), layout[1], table_state);
|
||||||
grouped_lines(app, result).block(Block::new().borders(Borders::RIGHT)),
|
|
||||||
layout[1],
|
|
||||||
table_state,
|
|
||||||
);
|
|
||||||
frame.render_stateful_widget(
|
|
||||||
scrollbar,
|
|
||||||
layout[1].inner(Margin {
|
|
||||||
vertical: 1,
|
|
||||||
horizontal: 0,
|
|
||||||
}),
|
|
||||||
scroll_state,
|
|
||||||
);
|
|
||||||
frame.render_widget(footer(app, page), layout[2]);
|
frame.render_widget(footer(app, page), layout[2]);
|
||||||
}
|
}
|
||||||
UiState::Logs(LogsState {
|
UiState::Logs(LogsState {
|
||||||
lines,
|
lines, table_state, ..
|
||||||
table_state,
|
|
||||||
scroll_state,
|
|
||||||
..
|
|
||||||
}) => {
|
}) => {
|
||||||
let scrollbar = Scrollbar::new(ScrollbarOrientation::VerticalRight)
|
|
||||||
.begin_symbol(Some("↑"))
|
|
||||||
.end_symbol(Some("↓"));
|
|
||||||
frame.render_stateful_widget(
|
frame.render_stateful_widget(
|
||||||
raw_logs(app, lines).block(Block::new().borders(Borders::RIGHT)),
|
raw_logs(app, lines),
|
||||||
layout[0].union(layout[1]),
|
layout[0].union(layout[1]),
|
||||||
table_state,
|
table_state,
|
||||||
);
|
);
|
||||||
frame.render_stateful_widget(
|
|
||||||
scrollbar,
|
|
||||||
layout[0].union(layout[1]).inner(Margin {
|
|
||||||
vertical: 1,
|
|
||||||
horizontal: 0,
|
|
||||||
}),
|
|
||||||
scroll_state,
|
|
||||||
);
|
|
||||||
frame.render_widget(footer(app, page), layout[2]);
|
frame.render_widget(footer(app, page), layout[2]);
|
||||||
}
|
}
|
||||||
UiState::Log(LogState {
|
UiState::Log(LogState {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::logline::LogLine;
|
use crate::logline::LogLine;
|
||||||
use crate::ui::style::{TABLE_HEADER_STYLE, TABLE_SELECTED_STYLE, TIME_FORMAT};
|
use crate::ui::style::{TABLE_HEADER_STYLE, TIME_FORMAT};
|
||||||
|
use crate::ui::table::ScrollbarTable;
|
||||||
use ratatui::layout::{Alignment, Constraint};
|
use ratatui::layout::{Alignment, Constraint};
|
||||||
use ratatui::text::Text;
|
use ratatui::text::Text;
|
||||||
use ratatui::widgets::{Cell, HighlightSpacing, Row, Table};
|
use ratatui::widgets::{Cell, Row};
|
||||||
use time::format_description::well_known::Iso8601;
|
use time::format_description::well_known::Iso8601;
|
||||||
|
|
||||||
pub fn raw_logs(app: &App, lines: &[usize]) -> Table<'static> {
|
pub fn raw_logs(app: &App, lines: &[usize]) -> ScrollbarTable<'static> {
|
||||||
let lines = lines.iter().copied().map(|i| &app.lines[i]);
|
let lines = lines.iter().copied().map(|i| &app.lines[i]);
|
||||||
let header = [
|
let header = [
|
||||||
Text::from("Level"),
|
Text::from("Level"),
|
||||||
|
|
@ -26,11 +27,7 @@ pub fn raw_logs(app: &App, lines: &[usize]) -> Table<'static> {
|
||||||
Constraint::Percentage(100),
|
Constraint::Percentage(100),
|
||||||
Constraint::Length(27),
|
Constraint::Length(27),
|
||||||
];
|
];
|
||||||
let table = Table::new(lines.map(log_row), widths)
|
ScrollbarTable::new(lines.map(log_row), widths).header(header)
|
||||||
.header(header)
|
|
||||||
.highlight_style(TABLE_SELECTED_STYLE)
|
|
||||||
.highlight_spacing(HighlightSpacing::Always);
|
|
||||||
table
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn log_row(line: &LogLine) -> Row<'static> {
|
fn log_row(line: &LogLine) -> Row<'static> {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::logline::{FullException, FullLogLine, LogLine, Trace};
|
use crate::logline::{FullException, FullLogLine, LogLine, Trace};
|
||||||
use crate::ui::style::{TABLE_HEADER_STYLE, TABLE_SELECTED_STYLE, TIME_FORMAT};
|
use crate::ui::style::{TABLE_HEADER_STYLE, TIME_FORMAT};
|
||||||
|
use crate::ui::table::{ScrollbarTable, ScrollbarTableState};
|
||||||
use ratatui::prelude::*;
|
use ratatui::prelude::*;
|
||||||
use ratatui::widgets::{Cell, HighlightSpacing, Paragraph, Row, Table, TableState, Wrap};
|
use ratatui::widgets::{Cell, Paragraph, Row, Wrap};
|
||||||
use std::iter::once;
|
use std::iter::once;
|
||||||
use time::format_description::well_known::Iso8601;
|
use time::format_description::well_known::Iso8601;
|
||||||
|
|
||||||
|
|
@ -17,7 +18,7 @@ pub struct SingleLog {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StatefulWidget for SingleLog {
|
impl StatefulWidget for SingleLog {
|
||||||
type State = TableState;
|
type State = ScrollbarTableState;
|
||||||
|
|
||||||
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State)
|
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State)
|
||||||
where
|
where
|
||||||
|
|
@ -48,7 +49,7 @@ impl StatefulWidget for SingleLog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_exception(exception: &FullException) -> Table {
|
pub fn render_exception(exception: &FullException) -> ScrollbarTable {
|
||||||
let header = [
|
let header = [
|
||||||
Text::from("File"),
|
Text::from("File"),
|
||||||
Text::from("Line").alignment(Alignment::Right),
|
Text::from("Line").alignment(Alignment::Right),
|
||||||
|
|
@ -66,11 +67,7 @@ pub fn render_exception(exception: &FullException) -> Table {
|
||||||
Constraint::Percentage(60),
|
Constraint::Percentage(60),
|
||||||
];
|
];
|
||||||
let rows = exception.stack().flat_map(exception_trace);
|
let rows = exception.stack().flat_map(exception_trace);
|
||||||
let table = Table::new(rows, widths)
|
ScrollbarTable::new(rows, widths).header(header)
|
||||||
.header(header)
|
|
||||||
.highlight_style(TABLE_SELECTED_STYLE)
|
|
||||||
.highlight_spacing(HighlightSpacing::Always);
|
|
||||||
table
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exception_trace(exception: &FullException) -> impl Iterator<Item = Row> + '_ {
|
fn exception_trace(exception: &FullException) -> impl Iterator<Item = Row> + '_ {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
use crate::app::{App, GroupedLines, LogMatch};
|
use crate::app::{App, GroupedLines, LogMatch};
|
||||||
use crate::ui::histogram::sparkline;
|
use crate::ui::histogram::sparkline;
|
||||||
use crate::ui::style::{TABLE_HEADER_STYLE, TABLE_SELECTED_STYLE};
|
use crate::ui::style::TABLE_HEADER_STYLE;
|
||||||
|
use crate::ui::table::ScrollbarTable;
|
||||||
use ratatui::layout::Constraint;
|
use ratatui::layout::Constraint;
|
||||||
use ratatui::text::Text;
|
use ratatui::text::Text;
|
||||||
use ratatui::widgets::{Cell, HighlightSpacing, Row, Table};
|
use ratatui::widgets::{Cell, Row};
|
||||||
|
|
||||||
pub fn grouped_lines(app: &App, log_match: &LogMatch) -> Table<'static> {
|
pub fn grouped_lines(app: &App, log_match: &LogMatch) -> ScrollbarTable<'static> {
|
||||||
let grouped = &log_match.grouped;
|
let grouped = &log_match.grouped;
|
||||||
let header = [
|
let header = [
|
||||||
Text::from("Level"),
|
Text::from("Level"),
|
||||||
|
|
@ -27,11 +28,7 @@ pub fn grouped_lines(app: &App, log_match: &LogMatch) -> Table<'static> {
|
||||||
Constraint::Length(10),
|
Constraint::Length(10),
|
||||||
Constraint::Min(10),
|
Constraint::Min(10),
|
||||||
];
|
];
|
||||||
let table = Table::new(grouped.iter().map(|group| group_row(app, group)), widths)
|
ScrollbarTable::new(grouped.iter().map(|group| group_row(app, group)), widths).header(header)
|
||||||
.header(header)
|
|
||||||
.highlight_style(TABLE_SELECTED_STYLE)
|
|
||||||
.highlight_spacing(HighlightSpacing::Always);
|
|
||||||
table
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn group_row(app: &App, group: &GroupedLines) -> Row<'static> {
|
fn group_row(app: &App, group: &GroupedLines) -> Row<'static> {
|
||||||
|
|
|
||||||
108
src/ui/state.rs
108
src/ui/state.rs
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::app::{App, LogMatch};
|
use crate::app::{App, LogMatch};
|
||||||
use crate::copy_osc;
|
use crate::copy_osc;
|
||||||
use crate::logline::{FullLogLine, LogLine};
|
use crate::logline::{FullLogLine, LogLine};
|
||||||
|
use crate::ui::table::ScrollbarTableState;
|
||||||
use derive_more::From;
|
use derive_more::From;
|
||||||
use ratatui::widgets::{ScrollbarState, TableState};
|
use ratatui::widgets::TableState;
|
||||||
use table_state::TableStateExt;
|
|
||||||
|
|
||||||
#[derive(Clone, From)]
|
#[derive(Clone, From)]
|
||||||
pub enum UiState<'a> {
|
pub enum UiState<'a> {
|
||||||
|
|
@ -16,41 +16,38 @@ pub enum UiState<'a> {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct MatchListState {
|
pub struct MatchListState {
|
||||||
pub table_state: TableState,
|
pub table_state: ScrollbarTableState,
|
||||||
pub scroll_state: ScrollbarState,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MatchListState {
|
impl MatchListState {
|
||||||
fn selected(&self) -> usize {
|
fn selected(&self) -> usize {
|
||||||
self.table_state.selected().unwrap()
|
self.table_state.selected()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct MatchState<'a> {
|
pub struct MatchState<'a> {
|
||||||
pub result: &'a LogMatch,
|
pub result: &'a LogMatch,
|
||||||
pub table_state: TableState,
|
pub table_state: ScrollbarTableState,
|
||||||
pub scroll_state: ScrollbarState,
|
|
||||||
pub previous: Box<UiState<'a>>,
|
pub previous: Box<UiState<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> MatchState<'a> {
|
impl<'a> MatchState<'a> {
|
||||||
fn selected(&self) -> usize {
|
fn selected(&self) -> usize {
|
||||||
self.table_state.selected().unwrap()
|
self.table_state.selected()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct LogsState<'a> {
|
pub struct LogsState<'a> {
|
||||||
pub lines: &'a [usize],
|
pub lines: &'a [usize],
|
||||||
pub table_state: TableState,
|
pub table_state: ScrollbarTableState,
|
||||||
pub scroll_state: ScrollbarState,
|
|
||||||
pub previous: Box<UiState<'a>>,
|
pub previous: Box<UiState<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> LogsState<'a> {
|
impl<'a> LogsState<'a> {
|
||||||
fn selected(&self) -> usize {
|
fn selected(&self) -> usize {
|
||||||
self.table_state.selected().unwrap()
|
self.table_state.selected()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,7 +56,7 @@ pub struct LogState<'a> {
|
||||||
pub trace_len: usize,
|
pub trace_len: usize,
|
||||||
pub log: &'a LogLine,
|
pub log: &'a LogLine,
|
||||||
pub full_line: FullLogLine,
|
pub full_line: FullLogLine,
|
||||||
pub table_state: TableState,
|
pub table_state: ScrollbarTableState,
|
||||||
pub previous: Box<UiState<'a>>,
|
pub previous: Box<UiState<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,8 +65,7 @@ impl<'a> UiState<'a> {
|
||||||
let mut table_state = TableState::default();
|
let mut table_state = TableState::default();
|
||||||
table_state.select(Some(0));
|
table_state.select(Some(0));
|
||||||
UiState::MatchList(MatchListState {
|
UiState::MatchList(MatchListState {
|
||||||
table_state,
|
table_state: ScrollbarTableState::new(app.match_lines()),
|
||||||
scroll_state: ScrollbarState::new(app.match_lines()),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,7 +78,7 @@ impl<'a> UiState<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn table_state(&mut self) -> Option<&mut TableState> {
|
fn table_state(&mut self) -> Option<&mut ScrollbarTableState> {
|
||||||
match self {
|
match self {
|
||||||
UiState::MatchList(state) => Some(&mut state.table_state),
|
UiState::MatchList(state) => Some(&mut state.table_state),
|
||||||
UiState::Match(state) => Some(&mut state.table_state),
|
UiState::Match(state) => Some(&mut state.table_state),
|
||||||
|
|
@ -92,54 +88,25 @@ impl<'a> UiState<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scroll_state(&mut self) -> Option<&mut ScrollbarState> {
|
|
||||||
match self {
|
|
||||||
UiState::MatchList(state) => Some(&mut state.scroll_state),
|
|
||||||
UiState::Match(state) => Some(&mut state.scroll_state),
|
|
||||||
UiState::Logs(state) => Some(&mut state.scroll_state),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn row_count(&self, app: &App) -> usize {
|
|
||||||
match self {
|
|
||||||
UiState::MatchList(_) => app.match_lines(),
|
|
||||||
UiState::Match(state) => state.result.grouped.len(),
|
|
||||||
UiState::Logs(state) => state.lines.len(),
|
|
||||||
UiState::Log(state) => state.trace_len,
|
|
||||||
_ => 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn process(self, event: UiEvent, app: &'a App) -> (bool, UiState) {
|
pub fn process(self, event: UiEvent, app: &'a App) -> (bool, UiState) {
|
||||||
match (self, event) {
|
match (self, event) {
|
||||||
(UiState::Quit, _) => (true, UiState::Quit),
|
(UiState::Quit, _) => (true, UiState::Quit),
|
||||||
(_, UiEvent::Quit) => (true, UiState::Quit),
|
(_, UiEvent::Quit) => (true, UiState::Quit),
|
||||||
(UiState::MatchList(_), UiEvent::Back) => (true, UiState::Quit),
|
(UiState::MatchList(_), UiEvent::Back) => (true, UiState::Quit),
|
||||||
(mut state, UiEvent::Down(step)) => {
|
(mut state, UiEvent::Down(step)) => {
|
||||||
let count = state.row_count(app);
|
|
||||||
if let Some(table_state) = state.table_state() {
|
if let Some(table_state) = state.table_state() {
|
||||||
let pos = table_state.down(count, step);
|
table_state.down(step);
|
||||||
if let Some(scroll_state) = state.scroll_state() {
|
|
||||||
*scroll_state = scroll_state.position(pos);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
(true, state)
|
(true, state)
|
||||||
}
|
}
|
||||||
(mut state, UiEvent::Up(step)) => {
|
(mut state, UiEvent::Up(step)) => {
|
||||||
let count = state.row_count(app);
|
|
||||||
if let Some(table_state) = state.table_state() {
|
if let Some(table_state) = state.table_state() {
|
||||||
let pos = table_state.up(count, step);
|
table_state.up(step);
|
||||||
if let Some(scroll_state) = state.scroll_state() {
|
|
||||||
*scroll_state = scroll_state.position(pos);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
(true, state)
|
(true, state)
|
||||||
}
|
}
|
||||||
(UiState::MatchList(state), UiEvent::Select) => {
|
(UiState::MatchList(state), UiEvent::Select) => {
|
||||||
let selected = state.selected();
|
let selected = state.selected();
|
||||||
let mut table_state = TableState::default();
|
|
||||||
table_state.select(Some(0));
|
|
||||||
|
|
||||||
let result = if selected == 0 {
|
let result = if selected == 0 {
|
||||||
&app.all
|
&app.all
|
||||||
|
|
@ -148,12 +115,12 @@ impl<'a> UiState<'a> {
|
||||||
} else {
|
} else {
|
||||||
&app.matches[selected - 1]
|
&app.matches[selected - 1]
|
||||||
};
|
};
|
||||||
|
let table_state = ScrollbarTableState::new(result.grouped.len());
|
||||||
(
|
(
|
||||||
true,
|
true,
|
||||||
UiState::Match(MatchState {
|
UiState::Match(MatchState {
|
||||||
result,
|
result,
|
||||||
table_state,
|
table_state,
|
||||||
scroll_state: ScrollbarState::new(result.count()),
|
|
||||||
previous: Box::new(state.into()),
|
previous: Box::new(state.into()),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
@ -164,20 +131,18 @@ impl<'a> UiState<'a> {
|
||||||
table_state.select(Some(0));
|
table_state.select(Some(0));
|
||||||
|
|
||||||
let lines = state.result.grouped[selected].lines.as_slice();
|
let lines = state.result.grouped[selected].lines.as_slice();
|
||||||
|
let table_state = ScrollbarTableState::new(lines.len());
|
||||||
(
|
(
|
||||||
true,
|
true,
|
||||||
UiState::Logs(LogsState {
|
UiState::Logs(LogsState {
|
||||||
lines,
|
lines,
|
||||||
table_state,
|
table_state,
|
||||||
scroll_state: ScrollbarState::new(lines.len()),
|
|
||||||
previous: Box::new(state.into()),
|
previous: Box::new(state.into()),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(UiState::Logs(state), UiEvent::Select) => {
|
(UiState::Logs(state), UiEvent::Select) => {
|
||||||
let selected = state.selected();
|
let selected = state.selected();
|
||||||
let mut table_state = TableState::default();
|
|
||||||
table_state.select(Some(0));
|
|
||||||
|
|
||||||
let line = state.lines[selected];
|
let line = state.lines[selected];
|
||||||
let log = &app.lines[line];
|
let log = &app.lines[line];
|
||||||
|
|
@ -188,6 +153,8 @@ impl<'a> UiState<'a> {
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let table_state = ScrollbarTableState::new(trace_len);
|
||||||
(
|
(
|
||||||
true,
|
true,
|
||||||
UiState::Log(LogState {
|
UiState::Log(LogState {
|
||||||
|
|
@ -241,44 +208,3 @@ pub enum UiPage {
|
||||||
Logs,
|
Logs,
|
||||||
Log,
|
Log,
|
||||||
}
|
}
|
||||||
|
|
||||||
mod table_state {
|
|
||||||
use ratatui::widgets::TableState;
|
|
||||||
|
|
||||||
pub trait TableStateExt {
|
|
||||||
fn up(&mut self, count: usize, step: usize) -> usize;
|
|
||||||
fn down(&mut self, count: usize, step: usize) -> usize;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TableStateExt for TableState {
|
|
||||||
fn up(&mut self, count: usize, step: usize) -> usize {
|
|
||||||
let current = self.selected().unwrap_or(0);
|
|
||||||
let after = if step > current {
|
|
||||||
if step == 1 {
|
|
||||||
count - 1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
current - step
|
|
||||||
};
|
|
||||||
self.select(Some(after));
|
|
||||||
after
|
|
||||||
}
|
|
||||||
|
|
||||||
fn down(&mut self, count: usize, step: usize) -> usize {
|
|
||||||
let current = self.selected().unwrap_or(0);
|
|
||||||
let after = if step >= count - current {
|
|
||||||
if step == 1 {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
count - 1
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
current + step
|
|
||||||
};
|
|
||||||
self.select(Some(after));
|
|
||||||
after
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
111
src/ui/table.rs
Normal file
111
src/ui/table.rs
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
use crate::ui::style::TABLE_SELECTED_STYLE;
|
||||||
|
use ratatui::buffer::Buffer;
|
||||||
|
use ratatui::layout::{Constraint, Margin, Rect};
|
||||||
|
use ratatui::widgets::{
|
||||||
|
Block, Borders, HighlightSpacing, Row, Scrollbar, ScrollbarOrientation, ScrollbarState,
|
||||||
|
StatefulWidget, Table, TableState,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct ScrollbarTable<'a> {
|
||||||
|
table: Table<'a>,
|
||||||
|
scrollbar: Scrollbar<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ScrollbarTable<'a> {
|
||||||
|
pub fn new<R, C>(rows: R, widths: C) -> Self
|
||||||
|
where
|
||||||
|
R: IntoIterator,
|
||||||
|
R::Item: Into<Row<'a>>,
|
||||||
|
C: IntoIterator,
|
||||||
|
C::Item: Into<Constraint>,
|
||||||
|
{
|
||||||
|
let rows: Vec<_> = rows.into_iter().collect();
|
||||||
|
ScrollbarTable {
|
||||||
|
table: Table::new(rows, widths)
|
||||||
|
.block(Block::new().borders(Borders::RIGHT))
|
||||||
|
.highlight_style(TABLE_SELECTED_STYLE)
|
||||||
|
.highlight_spacing(HighlightSpacing::Always),
|
||||||
|
scrollbar: Scrollbar::new(ScrollbarOrientation::VerticalRight)
|
||||||
|
.begin_symbol(Some("↑"))
|
||||||
|
.end_symbol(Some("↓")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use = "method moves the value of self and returns the modified value"]
|
||||||
|
pub fn header(mut self, header: Row<'a>) -> Self {
|
||||||
|
self.table = self.table.header(header);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ScrollbarTableState {
|
||||||
|
count: usize,
|
||||||
|
table: TableState,
|
||||||
|
scrollbar: ScrollbarState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScrollbarTableState {
|
||||||
|
pub fn new(count: usize) -> Self {
|
||||||
|
let mut table = TableState::new();
|
||||||
|
table.select(Some(0));
|
||||||
|
ScrollbarTableState {
|
||||||
|
count,
|
||||||
|
table,
|
||||||
|
scrollbar: ScrollbarState::new(count),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn selected(&self) -> usize {
|
||||||
|
self.table.selected().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn up(&mut self, step: usize) -> usize {
|
||||||
|
let current = self.table.selected().unwrap_or(0);
|
||||||
|
let after = if step > current {
|
||||||
|
if step == 1 {
|
||||||
|
self.count - 1
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
current - step
|
||||||
|
};
|
||||||
|
self.table.select(Some(after));
|
||||||
|
self.scrollbar = self.scrollbar.position(after);
|
||||||
|
after
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn down(&mut self, step: usize) -> usize {
|
||||||
|
let current = self.table.selected().unwrap_or(0);
|
||||||
|
let after = if step >= self.count - current {
|
||||||
|
if step == 1 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
self.count - 1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
current + step
|
||||||
|
};
|
||||||
|
self.table.select(Some(after));
|
||||||
|
self.scrollbar = self.scrollbar.position(after);
|
||||||
|
after
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> StatefulWidget for ScrollbarTable<'a> {
|
||||||
|
type State = ScrollbarTableState;
|
||||||
|
|
||||||
|
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
||||||
|
StatefulWidget::render(self.table, area, buf, &mut state.table);
|
||||||
|
StatefulWidget::render(
|
||||||
|
self.scrollbar,
|
||||||
|
area.inner(Margin {
|
||||||
|
vertical: 1,
|
||||||
|
horizontal: 0,
|
||||||
|
}),
|
||||||
|
buf,
|
||||||
|
&mut state.scrollbar,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue