mirror of
https://codeberg.org/icewind/logsmash.git
synced 2026-06-03 10:04:12 +02:00
scrollbar
This commit is contained in:
parent
59b55cbca7
commit
7a9fb10037
2 changed files with 97 additions and 21 deletions
|
|
@ -12,6 +12,7 @@ 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;
|
||||||
|
|
@ -29,7 +30,7 @@ pub fn run_ui(app: App) -> Result<(), UiError> {
|
||||||
stdout().execute(EnterAlternateScreen)?;
|
stdout().execute(EnterAlternateScreen)?;
|
||||||
let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
|
let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?;
|
||||||
|
|
||||||
let mut ui_state = UiState::default();
|
let mut ui_state = UiState::new(&app);
|
||||||
|
|
||||||
while !matches!(ui_state, UiState::Quit) {
|
while !matches!(ui_state, UiState::Quit) {
|
||||||
terminal.draw(|frame| ui(frame, &app, &mut ui_state))?;
|
terminal.draw(|frame| ui(frame, &app, &mut ui_state))?;
|
||||||
|
|
@ -79,7 +80,10 @@ fn ui(frame: &mut Frame, app: &App, state: &mut UiState) {
|
||||||
|
|
||||||
match state {
|
match state {
|
||||||
UiState::Quit => {}
|
UiState::Quit => {}
|
||||||
UiState::MatchList { table_state } => {
|
UiState::MatchList {
|
||||||
|
table_state,
|
||||||
|
scroll_state,
|
||||||
|
} => {
|
||||||
let selected = table_state.selected().unwrap_or(0);
|
let selected = table_state.selected().unwrap_or(0);
|
||||||
let histogram = if selected == 0 {
|
let histogram = if selected == 0 {
|
||||||
&app.all.histogram
|
&app.all.histogram
|
||||||
|
|
@ -89,28 +93,75 @@ 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(match_list(app), layout[1], table_state);
|
frame.render_stateful_widget(
|
||||||
|
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 {
|
UiState::Match {
|
||||||
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().unwrap_or_default()];
|
||||||
|
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(grouped_lines(app, result), layout[1], table_state);
|
frame.render_stateful_widget(
|
||||||
|
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 {
|
UiState::Logs {
|
||||||
lines, table_state, ..
|
lines,
|
||||||
|
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),
|
raw_logs(app, lines).block(Block::new().borders(Borders::RIGHT)),
|
||||||
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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,38 @@
|
||||||
use crate::app::{App, LogMatch};
|
use crate::app::{App, LogMatch};
|
||||||
use ratatui::widgets::TableState;
|
use ratatui::widgets::{ScrollbarState, TableState};
|
||||||
use table_state::TableStateExt;
|
use table_state::TableStateExt;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum UiState<'a> {
|
pub enum UiState<'a> {
|
||||||
MatchList {
|
MatchList {
|
||||||
table_state: TableState,
|
table_state: TableState,
|
||||||
|
scroll_state: ScrollbarState,
|
||||||
},
|
},
|
||||||
Match {
|
Match {
|
||||||
result: &'a LogMatch,
|
result: &'a LogMatch,
|
||||||
table_state: TableState,
|
table_state: TableState,
|
||||||
|
scroll_state: ScrollbarState,
|
||||||
previous: Box<UiState<'a>>,
|
previous: Box<UiState<'a>>,
|
||||||
},
|
},
|
||||||
Logs {
|
Logs {
|
||||||
lines: &'a [usize],
|
lines: &'a [usize],
|
||||||
table_state: TableState,
|
table_state: TableState,
|
||||||
|
scroll_state: ScrollbarState,
|
||||||
previous: Box<UiState<'a>>,
|
previous: Box<UiState<'a>>,
|
||||||
},
|
},
|
||||||
Quit,
|
Quit,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for UiState<'_> {
|
impl<'a> UiState<'a> {
|
||||||
fn default() -> Self {
|
pub fn new(app: &App) -> Self {
|
||||||
let mut table_state = TableState::default();
|
let mut table_state = TableState::default();
|
||||||
table_state.select(Some(0));
|
table_state.select(Some(0));
|
||||||
UiState::MatchList { table_state }
|
UiState::MatchList {
|
||||||
|
table_state,
|
||||||
|
scroll_state: ScrollbarState::new(app.match_lines()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> UiState<'a> {
|
|
||||||
pub fn page(&self) -> UiPage {
|
pub fn page(&self) -> UiPage {
|
||||||
match self {
|
match self {
|
||||||
UiState::Quit | UiState::MatchList { .. } => UiPage::MatchList,
|
UiState::Quit | UiState::MatchList { .. } => UiPage::MatchList,
|
||||||
|
|
@ -39,13 +43,22 @@ impl<'a> UiState<'a> {
|
||||||
|
|
||||||
fn table_state(&mut self) -> Option<&mut TableState> {
|
fn table_state(&mut self) -> Option<&mut TableState> {
|
||||||
match self {
|
match self {
|
||||||
UiState::MatchList { table_state } => Some(table_state),
|
UiState::MatchList { table_state, .. } => Some(table_state),
|
||||||
UiState::Match { table_state, .. } => Some(table_state),
|
UiState::Match { table_state, .. } => Some(table_state),
|
||||||
UiState::Logs { table_state, .. } => Some(table_state),
|
UiState::Logs { table_state, .. } => Some(table_state),
|
||||||
UiState::Quit => None,
|
UiState::Quit => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn scroll_state(&mut self) -> Option<&mut ScrollbarState> {
|
||||||
|
match self {
|
||||||
|
UiState::MatchList { scroll_state, .. } => Some(scroll_state),
|
||||||
|
UiState::Match { scroll_state, .. } => Some(scroll_state),
|
||||||
|
UiState::Logs { scroll_state, .. } => Some(scroll_state),
|
||||||
|
UiState::Quit => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn table_count(&self, app: &App) -> usize {
|
fn table_count(&self, app: &App) -> usize {
|
||||||
match self {
|
match self {
|
||||||
UiState::MatchList { .. } => app.match_lines(),
|
UiState::MatchList { .. } => app.match_lines(),
|
||||||
|
|
@ -63,20 +76,25 @@ impl<'a> UiState<'a> {
|
||||||
(mut state, UiEvent::Down(step)) => {
|
(mut state, UiEvent::Down(step)) => {
|
||||||
let count = state.table_count(app);
|
let count = state.table_count(app);
|
||||||
if let Some(table_state) = state.table_state() {
|
if let Some(table_state) = state.table_state() {
|
||||||
table_state.down(count, step)
|
let pos = table_state.down(count, step);
|
||||||
|
let scroll_state = state.scroll_state().unwrap();
|
||||||
|
*scroll_state = scroll_state.position(pos);
|
||||||
}
|
}
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
(mut state, UiEvent::Up(step)) => {
|
(mut state, UiEvent::Up(step)) => {
|
||||||
let count = state.table_count(app);
|
let count = state.table_count(app);
|
||||||
if let Some(table_state) = state.table_state() {
|
if let Some(table_state) = state.table_state() {
|
||||||
table_state.up(count, step)
|
let pos = table_state.up(count, step);
|
||||||
|
let scroll_state = state.scroll_state().unwrap();
|
||||||
|
*scroll_state = scroll_state.position(pos);
|
||||||
}
|
}
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
UiState::MatchList {
|
UiState::MatchList {
|
||||||
table_state: prev_state,
|
table_state: prev_state,
|
||||||
|
scroll_state: prev_scroll,
|
||||||
},
|
},
|
||||||
UiEvent::Select,
|
UiEvent::Select,
|
||||||
) => {
|
) => {
|
||||||
|
|
@ -94,14 +112,17 @@ impl<'a> UiState<'a> {
|
||||||
UiState::Match {
|
UiState::Match {
|
||||||
result,
|
result,
|
||||||
table_state,
|
table_state,
|
||||||
|
scroll_state: ScrollbarState::new(result.count()),
|
||||||
previous: Box::new(UiState::MatchList {
|
previous: Box::new(UiState::MatchList {
|
||||||
table_state: prev_state,
|
table_state: prev_state,
|
||||||
|
scroll_state: prev_scroll,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
UiState::Match {
|
UiState::Match {
|
||||||
table_state: prev_state,
|
table_state: prev_state,
|
||||||
|
scroll_state: prev_scroll,
|
||||||
previous,
|
previous,
|
||||||
result,
|
result,
|
||||||
},
|
},
|
||||||
|
|
@ -115,8 +136,10 @@ impl<'a> UiState<'a> {
|
||||||
UiState::Logs {
|
UiState::Logs {
|
||||||
lines,
|
lines,
|
||||||
table_state,
|
table_state,
|
||||||
|
scroll_state: ScrollbarState::new(lines.len()),
|
||||||
previous: Box::new(UiState::Match {
|
previous: Box::new(UiState::Match {
|
||||||
table_state: prev_state,
|
table_state: prev_state,
|
||||||
|
scroll_state: prev_scroll,
|
||||||
previous,
|
previous,
|
||||||
result,
|
result,
|
||||||
}),
|
}),
|
||||||
|
|
@ -148,29 +171,31 @@ mod table_state {
|
||||||
use ratatui::widgets::TableState;
|
use ratatui::widgets::TableState;
|
||||||
|
|
||||||
pub trait TableStateExt {
|
pub trait TableStateExt {
|
||||||
fn up(&mut self, count: usize, step: usize);
|
fn up(&mut self, count: usize, step: usize) -> usize;
|
||||||
fn down(&mut self, count: usize, step: usize);
|
fn down(&mut self, count: usize, step: usize) -> usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TableStateExt for TableState {
|
impl TableStateExt for TableState {
|
||||||
fn up(&mut self, count: usize, step: usize) {
|
fn up(&mut self, count: usize, step: usize) -> usize {
|
||||||
let current = self.selected().unwrap_or(0);
|
let current = self.selected().unwrap_or(0);
|
||||||
let after = if step > current {
|
let after = if step > current {
|
||||||
count - 1
|
count - 1
|
||||||
} else {
|
} else {
|
||||||
current - step
|
current - step
|
||||||
};
|
};
|
||||||
self.select(Some(after))
|
self.select(Some(after));
|
||||||
|
after
|
||||||
}
|
}
|
||||||
|
|
||||||
fn down(&mut self, count: usize, step: usize) {
|
fn down(&mut self, count: usize, step: usize) -> usize {
|
||||||
let current = self.selected().unwrap_or(0);
|
let current = self.selected().unwrap_or(0);
|
||||||
let after = if step >= count - current {
|
let after = if step >= count - current {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
current + step
|
current + step
|
||||||
};
|
};
|
||||||
self.select(Some(after))
|
self.select(Some(after));
|
||||||
|
after
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue