Skip to content

Commit

Permalink
refac(console): factor out Durations widget from task view (#408)
Browse files Browse the repository at this point in the history
There are 2 widgets which display the poll times for a task in the
detail view. The poll times percentiles are always displayed and if
UTF-8 is available, then a sparkline histogram is also shown to the
right.

The logic for displaying these two widgets is quite long and is
currently interspersed within the `render` function for the task detail
view plus helper functions. Additionally, it is not easy to add a second
set of widgets showing the time between waking and being polled for a
task which is planned for #409.

This change factors out that logic into separate widgets.

There was already a separate widget `MiniHistogram`. Some of the logic
that was previously in the task detail view has been moved here.

A new widget `Percentiles` has been added to encapsulate the logic for
preparing and displaying the percentiles.

A top level `Durations` widget occupies the entire width of the task
detail view and control the horizontal layout of the `Percentiles` and
`MiniHistogram` widgets. The new widget will also supress the histogram
if there isn't at least enough room to display the legend at the bottom
  • Loading branch information
hds authored Apr 13, 2023
1 parent 6fa2185 commit af6693b
Show file tree
Hide file tree
Showing 5 changed files with 347 additions and 230 deletions.
108 changes: 108 additions & 0 deletions tokio-console/src/view/durations.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
use std::cmp;

use tui::{
layout::{self},
widgets::Widget,
};

use crate::{
state::histogram::DurationHistogram,
view::{self, mini_histogram::MiniHistogram, percentiles::Percentiles},
};

// This is calculated so that a legend like the below generally fits:
// │0647.17µs 909.31µs │
// This also gives at characters for the sparkline itself.
const MIN_HISTOGRAM_BLOCK_WIDTH: u16 = 22;

/// This is a tui-rs widget to visualize durations as a list of percentiles
/// and if possible, a mini-histogram too.
///
/// This widget wraps the [`Percentiles`] and [`MiniHistogram`] widgets which
/// are displayed side by side. The mini-histogram will only be displayed if
/// a) UTF-8 support is enabled via [`Styles`]
/// b) There is at least a minimum width (22 characters to display the full
/// bottom legend) left after drawing the percentiles
///
/// This
///
/// [`Styles`]: crate::view::Styles
pub(crate) struct Durations<'a> {
/// Widget style
styles: &'a view::Styles,
/// The histogram data to render
histogram: Option<&'a DurationHistogram>,
/// Title for percentiles block
percentiles_title: &'a str,
/// Title for histogram sparkline block
histogram_title: &'a str,
}

impl<'a> Widget for Durations<'a> {
fn render(self, area: tui::layout::Rect, buf: &mut tui::buffer::Buffer) {
// Only split the durations area in half if we're also drawing a
// sparkline. We require UTF-8 to draw the sparkline and also enough width.
let (percentiles_area, histogram_area) = if self.styles.utf8 {
let percentiles_width = cmp::max(self.percentiles_title.len() as u16, 13_u16) + 2;

// If there isn't enough width left after drawing the percentiles
// then we won't draw the sparkline at all.
if area.width < percentiles_width + MIN_HISTOGRAM_BLOCK_WIDTH {
(area, None)
} else {
let areas = layout::Layout::default()
.direction(layout::Direction::Horizontal)
.constraints(
[
layout::Constraint::Length(percentiles_width),
layout::Constraint::Min(MIN_HISTOGRAM_BLOCK_WIDTH),
]
.as_ref(),
)
.split(area);
(areas[0], Some(areas[1]))
}
} else {
(area, None)
};

let percentiles_widget = Percentiles::new(self.styles)
.title(self.percentiles_title)
.histogram(self.histogram);
percentiles_widget.render(percentiles_area, buf);

if let Some(histogram_area) = histogram_area {
let histogram_widget = MiniHistogram::default()
.block(self.styles.border_block().title(self.histogram_title))
.histogram(self.histogram)
.duration_precision(2);
histogram_widget.render(histogram_area, buf);
}
}
}

impl<'a> Durations<'a> {
pub(crate) fn new(styles: &'a view::Styles) -> Self {
Self {
styles,
histogram: None,
percentiles_title: "Percentiles",
histogram_title: "Histogram",
}
}

pub(crate) fn histogram(mut self, histogram: Option<&'a DurationHistogram>) -> Self {
self.histogram = histogram;
self
}

pub(crate) fn percentiles_title(mut self, title: &'a str) -> Self {
self.percentiles_title = title;
self
}

pub(crate) fn histogram_title(mut self, title: &'a str) -> Self {
self.histogram_title = title;
self
}
}
Loading

0 comments on commit af6693b

Please sign in to comment.