diff --git a/tokio-console/src/input.rs b/tokio-console/src/input.rs index 8df557d1c..53fdba7b8 100644 --- a/tokio-console/src/input.rs +++ b/tokio-console/src/input.rs @@ -31,3 +31,13 @@ pub(crate) fn is_space(input: &Event) -> bool { }) ) } + +pub(crate) fn is_help_toggle(event: &Event) -> bool { + matches!( + event, + Event::Key(KeyEvent { + code: KeyCode::Char('?'), + .. + }) + ) +} diff --git a/tokio-console/src/view/help.rs b/tokio-console/src/view/help.rs new file mode 100644 index 000000000..2d02004b6 --- /dev/null +++ b/tokio-console/src/view/help.rs @@ -0,0 +1,70 @@ +use tui::{ + layout::{self, Constraint, Direction, Layout}, + text::Text, + widgets::{Clear, Paragraph, Wrap}, +}; + +use crate::{state::State, view}; + +/// Simple view for help popup +pub(crate) struct HelpView { + help_text: Option, +} + +impl HelpView +where + T: Into>, +{ + pub(super) fn new(help_text: T) -> Self { + HelpView { + help_text: Some(help_text), + } + } + + pub(crate) fn render( + &mut self, + styles: &view::Styles, + frame: &mut tui::terminal::Frame, + _area: layout::Rect, + _state: &mut State, + ) { + let r = frame.size(); + let content = self + .help_text + .take() + .expect("help_text should be initialized") + .into(); + + let popup_layout = Layout::default() + .direction(Direction::Vertical) + .constraints( + [ + Constraint::Percentage(40), + Constraint::Percentage(20), + Constraint::Percentage(40), + ] + .as_ref(), + ) + .split(r); + + let popup_area = Layout::default() + .direction(Direction::Horizontal) + .constraints( + [ + Constraint::Percentage(20), + Constraint::Percentage(60), + Constraint::Percentage(20), + ] + .as_ref(), + ) + .split(popup_layout[1])[1]; + + let display_text = Paragraph::new(content) + .block(styles.border_block().title("Help")) + .wrap(Wrap { trim: true }); + + // Clear the help block area and render the popup + frame.render_widget(Clear, popup_area); + frame.render_widget(display_text, popup_area); + } +} diff --git a/tokio-console/src/view/mod.rs b/tokio-console/src/view/mod.rs index e22d239af..ac6355119 100644 --- a/tokio-console/src/view/mod.rs +++ b/tokio-console/src/view/mod.rs @@ -1,3 +1,4 @@ +use crate::view::help::HelpView; use crate::view::{resources::ResourcesTable, table::TableListState, tasks::TasksTable}; use crate::{input, state::State}; use std::{borrow::Cow, cmp}; @@ -8,6 +9,7 @@ use tui::{ }; mod async_ops; +mod help; mod mini_histogram; mod resource; mod resources; @@ -15,8 +17,10 @@ mod styles; mod table; mod task; mod tasks; +use self::resource::ResourceView; pub(crate) use self::styles::{Palette, Styles}; pub(crate) use self::table::SortBy; +use self::task::TaskView; const DUR_LEN: usize = 10; // This data is only updated every second, so it doesn't make a ton of @@ -36,6 +40,7 @@ pub struct View { tasks_list: TableListState, resources_list: TableListState, state: ViewState, + show_help_toggle: bool, pub(crate) styles: Styles, } @@ -89,6 +94,7 @@ impl View { state: ViewState::TasksList, tasks_list: TableListState::::default(), resources_list: TableListState::::default(), + show_help_toggle: false, styles, } } @@ -96,6 +102,9 @@ impl View { pub(crate) fn update_input(&mut self, event: input::Event, state: &State) -> UpdateKind { use ViewState::*; let mut update_kind = UpdateKind::Other; + + self.handl_help_popup(event); + match self.state { TasksList => { // The enter key changes views, so handle here since we can @@ -168,32 +177,56 @@ impl View { update_kind } + fn handl_help_popup(&mut self, event: crossterm::event::Event) { + if input::is_help_toggle(&event) { + // TODO: Pause state if we are about to show the help toggle + self.show_help_toggle = !self.show_help_toggle; + } + if self.show_help_toggle && !input::is_help_toggle(&event) { + // We have some other key pressed; clear the help popup. + self.show_help_toggle = !self.show_help_toggle; + } + } + pub(crate) fn render( &mut self, frame: &mut tui::terminal::Frame, area: layout::Rect, state: &mut State, ) { + let mut help_content; match self.state { ViewState::TasksList => { self.tasks_list.render(&self.styles, frame, area, state, ()); + help_content = HelpView::new(TableListState::::render_help_content( + &self.styles, + )); } ViewState::ResourcesList => { self.resources_list .render(&self.styles, frame, area, state, ()); + help_content = HelpView::new( + TableListState::::render_help_content(&self.styles), + ); } ViewState::TaskInstance(ref mut view) => { let now = state .last_updated_at() .expect("task view implies we've received an update"); view.render(&self.styles, frame, area, now); + help_content = HelpView::new(TaskView::render_help_content(&self.styles)); } ViewState::ResourceInstance(ref mut view) => { view.render(&self.styles, frame, area, state); + help_content = HelpView::new(ResourceView::render_help_content(&self.styles)); } } state.retain_active(); + + if self.show_help_toggle { + help_content.render(&self.styles, frame, area, state); + } } pub(crate) fn current_view(&self) -> &ViewState { diff --git a/tokio-console/src/view/resource.rs b/tokio-console/src/view/resource.rs index 87129121d..1d2e1e01f 100644 --- a/tokio-console/src/view/resource.rs +++ b/tokio-console/src/view/resource.rs @@ -118,4 +118,8 @@ impl ResourceView { .render(styles, frame, async_ops_area, state, ctx); self.initial_render = false; } + + pub(in crate::view) fn render_help_content(_styles: &view::Styles) -> Spans<'static> { + Spans::from(vec![Span::raw("A view to display help data for a resource")]) + } } diff --git a/tokio-console/src/view/table.rs b/tokio-console/src/view/table.rs index fd124fb81..50520f74f 100644 --- a/tokio-console/src/view/table.rs +++ b/tokio-console/src/view/table.rs @@ -180,6 +180,30 @@ impl, const N: usize> TableListState { ) { T::render(self, styles, frame, area, state, ctx) } + + pub(in crate::view) fn render_help_content(styles: &view::Styles) -> Spans<'static> { + Spans::from(vec![ + Span::raw("controls: "), + bold(styles.if_utf8("\u{2190}\u{2192}", "left, right")), + Span::raw(" or "), + bold("h, l"), + text::Span::raw(" = select column (sort),\n"), + bold(styles.if_utf8("\u{2191}\u{2193}", "up, down")), + Span::raw(" or "), + bold("k, j"), + text::Span::raw(" = scroll, "), + bold(styles.if_utf8("\u{21B5}", "enter")), + text::Span::raw(" = view details, "), + bold("i"), + text::Span::raw(" = invert sort (highest/lowest), "), + bold("q"), + text::Span::raw(" = quit "), + bold("gg"), + text::Span::raw(" = scroll to top, "), + bold("G"), + text::Span::raw(" = scroll to bottom"), + ]) + } } impl Default for TableListState diff --git a/tokio-console/src/view/task.rs b/tokio-console/src/view/task.rs index 0ee97e8aa..4275e79ae 100644 --- a/tokio-console/src/view/task.rs +++ b/tokio-console/src/view/task.rs @@ -267,6 +267,10 @@ impl TaskView { frame.render_widget(fields_widget, fields_area); frame.render_widget(percentiles_widget, percentiles_area); } + + pub(in crate::view) fn render_help_content(_styles: &view::Styles) -> Spans<'static> { + Spans::from(vec![Span::raw("A view to diplay help data for a task")]) + } } impl Details {