Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(console): Add icon that sorting state #301

Merged
merged 10 commits into from
Mar 8, 2022
46 changes: 29 additions & 17 deletions tokio-console/src/view/async_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ pub(crate) struct AsyncOpsTableCtx {
pub(crate) resource_id: u64,
}

impl TableList for AsyncOpsTable {
impl TableList<9> for AsyncOpsTable {
type Row = AsyncOp;
type Sort = SortBy;
type Context = AsyncOpsTableCtx;

const HEADER: &'static [&'static str] = &[
const HEADER: &'static [&'static str; 9] = &[
"ID",
"Parent",
"Task",
Expand All @@ -42,8 +42,20 @@ impl TableList for AsyncOpsTable {
"Attributes",
];

const WIDTHS: &'static [usize; 9] = &[
Self::HEADER[0].len() + 1,
Self::HEADER[1].len() + 1,
Self::HEADER[2].len() + 1,
Self::HEADER[3].len() + 1,
Self::HEADER[4].len() + 1,
Self::HEADER[5].len() + 1,
Self::HEADER[6].len() + 1,
Self::HEADER[7].len() + 1,
Self::HEADER[8].len() + 1,
];

fn render<B: tui::backend::Backend>(
table_list_state: &mut TableListState<Self>,
table_list_state: &mut TableListState<Self, 9>,
styles: &view::Styles,
frame: &mut tui::terminal::Frame<B>,
area: layout::Rect,
Expand Down Expand Up @@ -86,11 +98,11 @@ impl TableList for AsyncOpsTable {
.sort_by
.sort(now, &mut table_list_state.sorted_items);

let mut id_width = view::Width::new(Self::HEADER[0].len() as u16);
let mut parent_width = view::Width::new(Self::HEADER[1].len() as u16);
let mut task_width = view::Width::new(Self::HEADER[2].len() as u16);
let mut source_width = view::Width::new(Self::HEADER[3].len() as u16);
let mut polls_width = view::Width::new(Self::HEADER[7].len() as u16);
let mut id_width = view::Width::new(Self::WIDTHS[0] as u16);
let mut parent_width = view::Width::new(Self::WIDTHS[1] as u16);
let mut task_width = view::Width::new(Self::WIDTHS[2] as u16);
let mut source_width = view::Width::new(Self::WIDTHS[3] as u16);
let mut polls_width = view::Width::new(Self::WIDTHS[7] as u16);

let dur_cell = |dur: std::time::Duration| -> Cell<'static> {
Cell::from(styles.time_units(format!(
Expand Down Expand Up @@ -153,22 +165,22 @@ impl TableList for AsyncOpsTable {
})
};

let (selected_style, header_style) = if let Some(cyan) = styles.color(Color::Cyan) {
(Style::default().fg(cyan), Style::default())
let header_style = if styles.color(Color::Cyan).is_some() {
Style::default()
} else {
(
Style::default().remove_modifier(style::Modifier::REVERSED),
Style::default().add_modifier(style::Modifier::REVERSED),
)
Style::default().add_modifier(style::Modifier::REVERSED)
};
let header_style = header_style.add_modifier(style::Modifier::BOLD);

let header = Row::new(Self::HEADER.iter().enumerate().map(|(idx, &value)| {
let cell = Cell::from(value);
if idx == table_list_state.selected_column {
cell.style(selected_style)
if table_list_state.sort_descending {
Cell::from(styles.ascending(value))
} else {
Cell::from(styles.descending(value))
}
} else {
cell
Cell::from(value)
}
}))
.height(1)
Expand Down
8 changes: 4 additions & 4 deletions tokio-console/src/view/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ pub struct View {
/// details view), we want to leave the task list's state the way we left it
/// --- e.g., if the user previously selected a particular sorting, we want
/// it to remain sorted that way when we return to it.
tasks_list: TableListState<TasksTable>,
resources_list: TableListState<ResourcesTable>,
tasks_list: TableListState<TasksTable, 11>,
resources_list: TableListState<ResourcesTable, 9>,
state: ViewState,
pub(crate) styles: Styles,
}
Expand Down Expand Up @@ -87,8 +87,8 @@ impl View {
pub fn new(styles: Styles) -> Self {
Self {
state: ViewState::TasksList,
tasks_list: TableListState::<TasksTable>::default(),
resources_list: TableListState::<ResourcesTable>::default(),
tasks_list: TableListState::<TasksTable, 11>::default(),
resources_list: TableListState::<ResourcesTable, 9>::default(),
styles,
}
}
Expand Down
4 changes: 2 additions & 2 deletions tokio-console/src/view/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ use tui::{

pub(crate) struct ResourceView {
resource: Rc<RefCell<Resource>>,
async_ops_table: TableListState<AsyncOpsTable>,
async_ops_table: TableListState<AsyncOpsTable, 9>,
initial_render: bool,
}

impl ResourceView {
pub(super) fn new(resource: Rc<RefCell<Resource>>) -> Self {
ResourceView {
resource,
async_ops_table: TableListState::<AsyncOpsTable>::default(),
async_ops_table: TableListState::<AsyncOpsTable, 9>::default(),
initial_render: true,
}
}
Expand Down
50 changes: 31 additions & 19 deletions tokio-console/src/view/resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ use tui::{
#[derive(Debug, Default)]
pub(crate) struct ResourcesTable {}

impl TableList for ResourcesTable {
impl TableList<9> for ResourcesTable {
type Row = Resource;
type Sort = SortBy;
type Context = ();

const HEADER: &'static [&'static str] = &[
const HEADER: &'static [&'static str; 9] = &[
"ID",
"Parent",
"Kind",
Expand All @@ -37,8 +37,20 @@ impl TableList for ResourcesTable {
"Attributes",
];

const WIDTHS: &'static [usize; 9] = &[
Self::HEADER[0].len() + 1,
Self::HEADER[1].len() + 1,
Self::HEADER[2].len() + 1,
Self::HEADER[3].len() + 1,
Self::HEADER[4].len() + 1,
Self::HEADER[5].len() + 1,
Self::HEADER[6].len() + 1,
Self::HEADER[7].len() + 1,
Self::HEADER[8].len() + 1,
];

fn render<B: tui::backend::Backend>(
table_list_state: &mut TableListState<Self>,
table_list_state: &mut TableListState<Self, 9>,
styles: &view::Styles,
frame: &mut tui::terminal::Frame<B>,
area: layout::Rect,
Expand All @@ -59,15 +71,15 @@ impl TableList for ResourcesTable {
.sort_by
.sort(now, &mut table_list_state.sorted_items);

let viz_len: u16 = Self::HEADER[6].len() as u16;
let viz_len: u16 = Self::WIDTHS[6] as u16;

let mut id_width = view::Width::new(Self::HEADER[0].len() as u16);
let mut parent_width = view::Width::new(Self::HEADER[1].len() as u16);
let mut id_width = view::Width::new(Self::WIDTHS[0] as u16);
let mut parent_width = view::Width::new(Self::WIDTHS[1] as u16);

let mut kind_width = view::Width::new(Self::HEADER[2].len() as u16);
let mut target_width = view::Width::new(Self::HEADER[4].len() as u16);
let mut type_width = view::Width::new(Self::HEADER[5].len() as u16);
let mut location_width = view::Width::new(Self::HEADER[7].len() as u16);
let mut kind_width = view::Width::new(Self::WIDTHS[2] as u16);
let mut target_width = view::Width::new(Self::WIDTHS[4] as u16);
let mut type_width = view::Width::new(Self::WIDTHS[5] as u16);
let mut location_width = view::Width::new(Self::WIDTHS[7] as u16);
Comment on lines +74 to +82
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The WIDTHS array seems fine to me, but, for what it's worth, think it would also be fine to just change this code to something like

        let viz_len: u16 = (Self::HEADER[6].len() + 1) as u16;

        let mut id_width = view::Width::new((Self::HEADER[0].len() + 1) as u16);
        let mut parent_width = view::Width::new((Self::HEADER[1].len() + 1) as u16);
        // ... and so on ...

Since the HEADER array is a const, and the 1 is also a constant, this calculation should probably get const-folded, so I don't think there would be a performance difference between this approach and creating a separate const array. I think the code might be a bit simpler if we avoided the separate array.

But, this is fine either way --- it's not a big deal.


let rows = {
let id_width = &mut id_width;
Expand Down Expand Up @@ -120,22 +132,22 @@ impl TableList for ResourcesTable {
})
};

let (selected_style, header_style) = if let Some(cyan) = styles.color(Color::Cyan) {
(Style::default().fg(cyan), Style::default())
let header_style = if styles.color(Color::Cyan).is_some() {
Style::default()
} else {
(
Style::default().remove_modifier(style::Modifier::REVERSED),
Style::default().add_modifier(style::Modifier::REVERSED),
)
Style::default().add_modifier(style::Modifier::REVERSED)
};
let header_style = header_style.add_modifier(style::Modifier::BOLD);

let header = Row::new(Self::HEADER.iter().enumerate().map(|(idx, &value)| {
let cell = Cell::from(value);
if idx == table_list_state.selected_column {
cell.style(selected_style)
if table_list_state.sort_descending {
Cell::from(styles.ascending(value))
} else {
Cell::from(styles.descending(value))
}
} else {
cell
Cell::from(value)
}
}))
.height(1)
Expand Down
19 changes: 19 additions & 0 deletions tokio-console/src/view/styles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,25 @@ impl Styles {
)
}

pub fn selected<'a>(&self, value: &str) -> Span<'static> {
nrskt marked this conversation as resolved.
Show resolved Hide resolved
let style = if let Some(cyan) = self.color(Color::Cyan) {
Style::default().fg(cyan)
} else {
Style::default().remove_modifier(Modifier::REVERSED)
};
Span::styled(value.to_string(), style)
}

pub fn ascending(&self, value: &str) -> Span<'static> {
let value = format!("{}{}", value, self.if_utf8("▵", "+"));
self.selected(&value)
}

pub fn descending(&self, value: &str) -> Span<'static> {
let value = format!("{}{}", value, self.if_utf8("▿", "-"));
Comment on lines +140 to +145
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think it might look a little bit nicer if we added a space after the symbol in the case where it's a plus or minus sign, but not a blocker.

self.selected(&value)
}

pub fn color(&self, color: Color) -> Option<Color> {
use Palette::*;
match (self.palette, color) {
Expand Down
15 changes: 8 additions & 7 deletions tokio-console/src/view/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,16 @@ use tui::{
use std::cell::RefCell;
use std::rc::Weak;

pub(crate) trait TableList {
pub(crate) trait TableList<const N: usize> {
type Row;
type Sort: SortBy + TryFrom<usize>;
type Context;

const HEADER: &'static [&'static str];
const HEADER: &'static [&'static str; N];
const WIDTHS: &'static [usize; N];

fn render<B: tui::backend::Backend>(
state: &mut TableListState<Self>,
state: &mut TableListState<Self, N>,
styles: &view::Styles,
frame: &mut tui::terminal::Frame<B>,
area: layout::Rect,
Expand All @@ -34,7 +35,7 @@ pub(crate) trait SortBy {
fn as_column(&self) -> usize;
}

pub(crate) struct TableListState<T: TableList> {
pub(crate) struct TableListState<T: TableList<N>, const N: usize> {
pub(crate) sorted_items: Vec<Weak<RefCell<T::Row>>>,
pub(crate) sort_by: T::Sort,
pub(crate) selected_column: usize,
Expand All @@ -49,7 +50,7 @@ pub(crate) struct Controls {
pub(crate) height: u16,
}

impl<T: TableList> TableListState<T> {
impl<T: TableList<N>, const N: usize> TableListState<T, N> {
pub(in crate::view) fn len(&self) -> usize {
self.sorted_items.len()
}
Expand Down Expand Up @@ -181,9 +182,9 @@ impl<T: TableList> TableListState<T> {
}
}

impl<T> Default for TableListState<T>
impl<T, const N: usize> Default for TableListState<T, N>
where
T: TableList,
T: TableList<N>,
T::Sort: Default,
{
fn default() -> Self {
Expand Down
53 changes: 34 additions & 19 deletions tokio-console/src/view/tasks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,39 @@ use tui::{
#[derive(Debug, Default)]
pub(crate) struct TasksTable {}

impl TableList for TasksTable {
impl TableList<11> for TasksTable {
type Row = Task;
type Sort = SortBy;
type Context = ();

const HEADER: &'static [&'static str] = &[
const HEADER: &'static [&'static str; 11] = &[
"Warn", "ID", "State", "Name", "Total", "Busy", "Idle", "Polls", "Target", "Location",
"Fields",
];

const WIDTHS: &'static [usize; 11] = &[
Self::HEADER[0].len() + 1,
Self::HEADER[1].len() + 1,
Self::HEADER[2].len() + 1,
Self::HEADER[3].len() + 1,
Self::HEADER[4].len() + 1,
Self::HEADER[5].len() + 1,
Self::HEADER[6].len() + 1,
Self::HEADER[7].len() + 1,
Self::HEADER[8].len() + 1,
Self::HEADER[9].len() + 1,
Self::HEADER[10].len() + 1,
];

fn render<B: tui::backend::Backend>(
table_list_state: &mut TableListState<Self>,
table_list_state: &mut TableListState<Self, 11>,
styles: &view::Styles,
frame: &mut tui::terminal::Frame<B>,
area: layout::Rect,
state: &mut State,
_: Self::Context,
) {
let state_len: u16 = Self::HEADER[2].len() as u16;
let state_len: u16 = Self::WIDTHS[2] as u16;
let now = if let Some(now) = state.last_updated_at() {
now
} else {
Expand All @@ -63,15 +77,16 @@ impl TableList for TasksTable {
};

// Start out wide enough to display the column headers...
let mut warn_width = view::Width::new(Self::HEADER[0].len() as u16);
let mut id_width = view::Width::new(Self::HEADER[1].len() as u16);
let mut name_width = view::Width::new(Self::HEADER[3].len() as u16);
let mut polls_width = view::Width::new(Self::HEADER[7].len() as u16);
let mut target_width = view::Width::new(Self::HEADER[8].len() as u16);
let mut location_width = view::Width::new(Self::HEADER[9].len() as u16);
let mut warn_width = view::Width::new(Self::WIDTHS[0] as u16);
let mut id_width = view::Width::new(Self::WIDTHS[1] as u16);
let mut name_width = view::Width::new(Self::WIDTHS[3] as u16);
let mut polls_width = view::Width::new(Self::WIDTHS[7] as u16);
let mut target_width = view::Width::new(Self::WIDTHS[8] as u16);
let mut location_width = view::Width::new(Self::WIDTHS[9] as u16);

let mut num_idle = 0;
let mut num_running = 0;

let rows = {
let id_width = &mut id_width;
let target_width = &mut target_width;
Expand Down Expand Up @@ -138,22 +153,22 @@ impl TableList for TasksTable {
})
};

let (selected_style, header_style) = if let Some(cyan) = styles.color(Color::Cyan) {
(Style::default().fg(cyan), Style::default())
let header_style = if styles.color(Color::Cyan).is_some() {
Style::default()
} else {
(
Style::default().remove_modifier(style::Modifier::REVERSED),
Style::default().add_modifier(style::Modifier::REVERSED),
)
Style::default().add_modifier(style::Modifier::REVERSED)
};
let header_style = header_style.add_modifier(style::Modifier::BOLD);

let header = Row::new(Self::HEADER.iter().enumerate().map(|(idx, &value)| {
let cell = Cell::from(value);
if idx == table_list_state.selected_column {
cell.style(selected_style)
if table_list_state.sort_descending {
Cell::from(styles.ascending(value))
} else {
Cell::from(styles.descending(value))
}
} else {
cell
Cell::from(value)
}
}))
.height(1)
Expand Down