diff --git a/docs/content/usage/general-usage.md b/docs/content/usage/general-usage.md
index 4bfcdb61..eb457aef 100644
--- a/docs/content/usage/general-usage.md
+++ b/docs/content/usage/general-usage.md
@@ -41,8 +41,8 @@ Note that key bindings are generally case-sensitive.
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| ++q++ , ++ctrl+c++ | Quit |
| ++esc++ | Close dialog windows, search, widgets, or exit expanded mode |
-| ++ctrl+r++ | Reset display and any collected data |
-| ++f++ | Freeze/unfreeze updating with new data |
+| ++ctrl+r++ | Resets any collected data |
+| ++f++ | Toggles freezing, which stops new data from being shown |
| ++question++ | Open help menu |
| ++e++ | Toggle expanding the currently selected widget |
| ++ctrl+up++
++shift+up++
++K++
++W++ | Select the widget above |
diff --git a/src/app.rs b/src/app.rs
index 7e2451b6..b3409d3f 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -104,19 +104,29 @@ pub struct AppConfigFields {
pub no_write: bool,
pub show_table_scroll_position: bool,
pub is_advanced_kill: bool,
- // TODO: Remove these, move network details state-side.
pub network_unit_type: DataUnit,
pub network_scale_type: AxisScaling,
pub network_use_binary_prefix: bool,
}
+/// The [`FrozenState`] indicates whether the application state should be frozen; if it is, save a snapshot of
+/// the data collected at that instant.
+pub enum FrozenState {
+ NotFrozen,
+ Frozen(DataCollection),
+}
+
+impl Default for FrozenState {
+ fn default() -> Self {
+ Self::NotFrozen
+ }
+}
+
pub struct AppState {
pub dd_err: Option,
to_delete_process_list: Option<(String, Vec)>,
- pub is_frozen: bool,
-
pub canvas_data: canvas::DisplayableData,
pub data_collection: DataCollection,
@@ -161,6 +171,7 @@ pub struct AppState {
pub widget_lookup_map: FxHashMap,
pub layout_tree: Arena,
pub layout_tree_root: NodeId,
+ frozen_state: FrozenState,
}
impl AppState {
@@ -189,7 +200,6 @@ impl AppState {
// Use defaults.
dd_err: Default::default(),
to_delete_process_list: Default::default(),
- is_frozen: Default::default(),
canvas_data: Default::default(),
data_collection: Default::default(),
is_expanded: Default::default(),
@@ -211,37 +221,30 @@ impl AppState {
basic_mode_use_percent: Default::default(),
is_force_redraw: Default::default(),
is_determining_widget_boundary: Default::default(),
+ frozen_state: Default::default(),
+ }
+ }
+
+ pub fn is_frozen(&self) -> bool {
+ matches!(self.frozen_state, FrozenState::Frozen(_))
+ }
+
+ pub fn toggle_freeze(&mut self) {
+ if matches!(self.frozen_state, FrozenState::Frozen(_)) {
+ self.frozen_state = FrozenState::NotFrozen;
+ } else {
+ self.frozen_state = FrozenState::Frozen(self.data_collection.clone());
}
}
pub fn reset(&mut self) {
- // Reset multi
- self.reset_multi_tap_keys();
-
- // Reset dialog state
- self.help_dialog_state.is_showing_help = false;
- self.delete_dialog_state.is_showing_dd = false;
-
- // Close all searches and reset it
- self.proc_state
- .widget_states
+ // Call reset on all widgets.
+ self.widget_lookup_map
.values_mut()
- .for_each(|state| {
- state.process_search_state.search_state.reset();
- });
- self.proc_state.force_update_all = true;
-
- // Clear current delete list
- self.to_delete_process_list = None;
- self.dd_err = None;
+ .for_each(|widget| widget.reset());
// Unfreeze.
- self.is_frozen = false;
-
- // Reset zoom
- self.reset_cpu_zoom();
- self.reset_mem_zoom();
- self.reset_net_zoom();
+ self.frozen_state = FrozenState::NotFrozen;
// Reset data
self.data_collection.reset();
@@ -259,10 +262,77 @@ impl AppState {
self.dd_err = None;
}
+ /// Handles a global event involving a char.
+ fn handle_global_char(&mut self, c: char) -> EventResult {
+ if c.is_ascii_control() {
+ EventResult::NoRedraw
+ } else {
+ // Check for case-sensitive bindings first.
+ match c {
+ 'H' | 'A' => self.move_to_widget(MovementDirection::Left),
+ 'L' | 'D' => self.move_to_widget(MovementDirection::Right),
+ 'K' | 'W' => self.move_to_widget(MovementDirection::Up),
+ 'J' | 'S' => self.move_to_widget(MovementDirection::Down),
+ _ => {
+ let c = c.to_ascii_lowercase();
+ match c {
+ 'q' => EventResult::Quit,
+ 'e' => {
+ if self.app_config_fields.use_basic_mode {
+ EventResult::NoRedraw
+ } else {
+ self.is_expanded = !self.is_expanded;
+ EventResult::Redraw
+ }
+ }
+ '?' => {
+ self.help_dialog_state.is_showing_help = true;
+ EventResult::Redraw
+ }
+ 'f' => {
+ self.toggle_freeze();
+ if !self.is_frozen() {
+ let data_collection = &self.data_collection;
+ self.widget_lookup_map
+ .iter_mut()
+ .for_each(|(_id, widget)| widget.update_data(data_collection));
+ }
+ EventResult::Redraw
+ }
+ _ => EventResult::NoRedraw,
+ }
+ }
+ }
+ }
+ }
+
+ /// Moves to a widget.
+ fn move_to_widget(&mut self, direction: MovementDirection) -> EventResult {
+ let layout_tree = &mut self.layout_tree;
+ let previous_selected = self.selected_widget;
+ if let Some(widget) = self.widget_lookup_map.get_mut(&self.selected_widget) {
+ match move_widget_selection(layout_tree, widget, self.selected_widget, direction) {
+ MoveWidgetResult::ForceRedraw(new_widget_id) => {
+ self.selected_widget = new_widget_id;
+ EventResult::Redraw
+ }
+ MoveWidgetResult::NodeId(new_widget_id) => {
+ self.selected_widget = new_widget_id;
+
+ if previous_selected != self.selected_widget {
+ EventResult::Redraw
+ } else {
+ EventResult::NoRedraw
+ }
+ }
+ }
+ } else {
+ EventResult::NoRedraw
+ }
+ }
+
/// Handles a global [`KeyEvent`], and returns an [`EventResult`].
fn handle_global_shortcut(&mut self, event: KeyEvent) -> EventResult {
- // TODO: Write this.
-
if event.modifiers.is_empty() {
match event.code {
KeyCode::Esc => {
@@ -280,24 +350,33 @@ impl AppState {
EventResult::NoRedraw
}
}
- KeyCode::Char('q') => EventResult::Quit,
- KeyCode::Char('e') => {
- if self.app_config_fields.use_basic_mode {
- EventResult::NoRedraw
- } else {
- self.is_expanded = !self.is_expanded;
- EventResult::Redraw
- }
- }
- KeyCode::Char('?') => {
- self.help_dialog_state.is_showing_help = true;
- EventResult::Redraw
- }
+ KeyCode::Char(c) => self.handle_global_char(c),
_ => EventResult::NoRedraw,
}
} else if let KeyModifiers::CONTROL = event.modifiers {
match event.code {
- KeyCode::Char('c') => EventResult::Quit,
+ KeyCode::Char('c') | KeyCode::Char('C') => EventResult::Quit,
+ KeyCode::Char('r') | KeyCode::Char('R') => {
+ self.reset();
+ let data_collection = &self.data_collection;
+ self.widget_lookup_map
+ .iter_mut()
+ .for_each(|(_id, widget)| widget.update_data(data_collection));
+ EventResult::Redraw
+ }
+ KeyCode::Left => self.move_to_widget(MovementDirection::Left),
+ KeyCode::Right => self.move_to_widget(MovementDirection::Right),
+ KeyCode::Up => self.move_to_widget(MovementDirection::Up),
+ KeyCode::Down => self.move_to_widget(MovementDirection::Down),
+ _ => EventResult::NoRedraw,
+ }
+ } else if let KeyModifiers::SHIFT = event.modifiers {
+ match event.code {
+ KeyCode::Left => self.move_to_widget(MovementDirection::Left),
+ KeyCode::Right => self.move_to_widget(MovementDirection::Right),
+ KeyCode::Up => self.move_to_widget(MovementDirection::Up),
+ KeyCode::Down => self.move_to_widget(MovementDirection::Down),
+ KeyCode::Char(c) => self.handle_global_char(c),
_ => EventResult::NoRedraw,
}
} else {
@@ -317,7 +396,14 @@ impl AppState {
}
ReturnSignal::Update => {
if let Some(widget) = self.widget_lookup_map.get_mut(&self.selected_widget) {
- widget.update_data(&self.data_collection);
+ match &self.frozen_state {
+ FrozenState::NotFrozen => {
+ widget.update_data(&self.data_collection);
+ }
+ FrozenState::Frozen(frozen_data) => {
+ widget.update_data(frozen_data);
+ }
+ }
}
EventResult::Redraw
}
@@ -379,7 +465,11 @@ impl AppState {
if was_id_already_selected {
return self.convert_widget_event_result(result);
} else {
- // If the weren't equal, *force* a redraw.
+ // If the weren't equal, *force* a redraw, and correct the layout tree.
+ correct_layout_last_selections(
+ &mut self.layout_tree,
+ self.selected_widget,
+ );
let _ = self.convert_widget_event_result(result);
return EventResult::Redraw;
}
@@ -402,7 +492,7 @@ impl AppState {
BottomEvent::Update(new_data) => {
self.data_collection.eat_data(new_data);
- if !self.is_frozen {
+ if !self.is_frozen() {
let data_collection = &self.data_collection;
self.widget_lookup_map
.iter_mut()
@@ -1599,12 +1689,7 @@ impl AppState {
'G' => self.skip_to_last(),
'k' => self.on_up_key(),
'j' => self.on_down_key(),
- 'f' => {
- self.is_frozen = !self.is_frozen;
- if self.is_frozen {
- self.data_collection.set_frozen_time();
- }
- }
+ 'f' => {}
'C' => {
// self.open_config(),
}
diff --git a/src/app/data_farmer.rs b/src/app/data_farmer.rs
index 25d265ab..c2747289 100644
--- a/src/app/data_farmer.rs
+++ b/src/app/data_farmer.rs
@@ -26,7 +26,7 @@ use regex::Regex;
pub type TimeOffset = f64;
pub type Value = f64;
-#[derive(Debug, Default)]
+#[derive(Clone, Debug, Default)]
pub struct TimedData {
pub rx_data: Value,
pub tx_data: Value,
@@ -41,14 +41,14 @@ pub struct TimedData {
/// collected, and what is needed to convert into a displayable form.
///
/// If the app is *frozen* - that is, we do not want to *display* any changing
-/// data, keep updating this, don't convert to canvas displayable data!
+/// data, keep updating this. As of 2021-09-08, we just clone the current collection
+/// when it freezes to have a snapshot floating around.
///
/// Note that with this method, the *app* thread is responsible for cleaning -
/// not the data collector.
-#[derive(Debug)]
+#[derive(Clone, Debug)]
pub struct DataCollection {
pub current_instant: Instant,
- pub frozen_instant: Option,
pub timed_data_vec: Vec<(Instant, TimedData)>,
pub network_harvest: network::NetworkHarvest,
pub memory_harvest: memory::MemHarvest,
@@ -70,7 +70,6 @@ impl Default for DataCollection {
fn default() -> Self {
DataCollection {
current_instant: Instant::now(),
- frozen_instant: None,
timed_data_vec: Vec::default(),
network_harvest: network::NetworkHarvest::default(),
memory_harvest: memory::MemHarvest::default(),
@@ -92,21 +91,19 @@ impl Default for DataCollection {
impl DataCollection {
pub fn reset(&mut self) {
- self.timed_data_vec = Vec::default();
- self.network_harvest = network::NetworkHarvest::default();
- self.memory_harvest = memory::MemHarvest::default();
- self.swap_harvest = memory::MemHarvest::default();
- self.cpu_harvest = cpu::CpuHarvest::default();
- self.process_harvest = Vec::default();
- self.disk_harvest = Vec::default();
- self.io_harvest = disks::IoHarvest::default();
- self.io_labels_and_prev = Vec::default();
- self.temp_harvest = Vec::default();
- self.battery_harvest = Vec::default();
- }
-
- pub fn set_frozen_time(&mut self) {
- self.frozen_instant = Some(self.current_instant);
+ self.timed_data_vec = Default::default();
+ self.network_harvest = Default::default();
+ self.memory_harvest = Default::default();
+ self.swap_harvest = Default::default();
+ self.cpu_harvest = Default::default();
+ self.process_harvest = Default::default();
+ self.process_name_pid_map = Default::default();
+ self.process_cmd_pid_map = Default::default();
+ self.disk_harvest = Default::default();
+ self.io_harvest = Default::default();
+ self.io_labels_and_prev = Default::default();
+ self.temp_harvest = Default::default();
+ self.battery_harvest = Default::default();
}
pub fn clean_data(&mut self, max_time_millis: u64) {
diff --git a/src/app/event.rs b/src/app/event.rs
index a6afb2d8..9201e2c0 100644
--- a/src/app/event.rs
+++ b/src/app/event.rs
@@ -45,7 +45,7 @@ pub enum WidgetEventResult {
/// How a widget should handle a widget selection request.
pub enum SelectionAction {
- /// This event occurs if the widget internally handled the selection action.
+ /// This event occurs if the widget internally handled the selection action. A redraw is required.
Handled,
/// This event occurs if the widget did not handle the selection action; the caller must handle it.
NotHandled,
diff --git a/src/app/layout_manager.rs b/src/app/layout_manager.rs
index 9b88fb8a..61d17924 100644
--- a/src/app/layout_manager.rs
+++ b/src/app/layout_manager.rs
@@ -997,9 +997,9 @@ Supported widget names:
// --- New stuff ---
/// Represents a row in the layout tree.
-#[derive(PartialEq, Eq, Clone)]
+#[derive(Debug, PartialEq, Eq, Clone)]
pub struct RowLayout {
- last_selected_index: usize,
+ last_selected: Option,
pub parent_rule: LayoutRule,
pub bound: Rect,
}
@@ -1007,7 +1007,7 @@ pub struct RowLayout {
impl RowLayout {
fn new(parent_rule: LayoutRule) -> Self {
Self {
- last_selected_index: 0,
+ last_selected: None,
parent_rule,
bound: Rect::default(),
}
@@ -1015,9 +1015,9 @@ impl RowLayout {
}
/// Represents a column in the layout tree.
-#[derive(PartialEq, Eq, Clone)]
+#[derive(Debug, PartialEq, Eq, Clone)]
pub struct ColLayout {
- last_selected_index: usize,
+ last_selected: Option,
pub parent_rule: LayoutRule,
pub bound: Rect,
}
@@ -1025,7 +1025,7 @@ pub struct ColLayout {
impl ColLayout {
fn new(parent_rule: LayoutRule) -> Self {
Self {
- last_selected_index: 0,
+ last_selected: None,
parent_rule,
bound: Rect::default(),
}
@@ -1033,7 +1033,7 @@ impl ColLayout {
}
/// Represents a widget in the layout tree.
-#[derive(PartialEq, Eq, Clone, Default)]
+#[derive(Debug, PartialEq, Eq, Clone, Default)]
pub struct WidgetLayout {
pub bound: Rect,
}
@@ -1042,7 +1042,7 @@ pub struct WidgetLayout {
/// - [`LayoutNode::Row`] (a non-leaf that distributes its children horizontally)
/// - [`LayoutNode::Col`] (a non-leaf node that distributes its children vertically)
/// - [`LayoutNode::Widget`] (a leaf node that contains the ID of the widget it is associated with)
-#[derive(PartialEq, Eq, Clone)]
+#[derive(Debug, PartialEq, Eq, Clone)]
pub enum LayoutNode {
/// A non-leaf that distributes its children horizontally
Row(RowLayout),
@@ -1382,6 +1382,8 @@ pub fn create_layout_tree(
));
}
+ correct_layout_last_selections(&mut arena, selected);
+
Ok(LayoutCreationOutput {
layout_tree: arena,
root: root_id,
@@ -1391,6 +1393,35 @@ pub fn create_layout_tree(
})
}
+/// We may have situations where we also have to make sure the correct layout indices are selected.
+/// For example, when we select a widget by clicking, we want to update the layout so that it's as if a user
+/// manually moved to it via keybinds.
+///
+/// We can do this by just going through the ancestors, starting from the widget itself.
+pub fn correct_layout_last_selections(arena: &mut Arena, selected: NodeId) {
+ let mut selected_ancestors = selected.ancestors(&arena).collect::>();
+ let prev_node = selected_ancestors.pop();
+ if let Some(mut prev_node) = prev_node {
+ for node in selected_ancestors {
+ if let Some(layout_node) = arena.get_mut(node).map(|n| n.get_mut()) {
+ match layout_node {
+ LayoutNode::Row(RowLayout { last_selected, .. })
+ | LayoutNode::Col(ColLayout { last_selected, .. }) => {
+ *last_selected = Some(prev_node);
+ }
+ LayoutNode::Widget(_) => {}
+ }
+ }
+ prev_node = node;
+ }
+ }
+}
+
+pub enum MoveWidgetResult {
+ ForceRedraw(NodeId),
+ NodeId(NodeId),
+}
+
/// Attempts to find and return the selected [`BottomWidgetId`] after moving in a direction.
///
/// Note this function assumes a properly built tree - if not, bad things may happen! We generally assume that:
@@ -1399,7 +1430,7 @@ pub fn create_layout_tree(
pub fn move_widget_selection(
layout_tree: &mut Arena, current_widget: &mut TmpBottomWidget,
current_widget_id: NodeId, direction: MovementDirection,
-) -> NodeId {
+) -> MoveWidgetResult {
// We first give our currently-selected widget a chance to react to the movement - it may handle it internally!
let handled = match direction {
MovementDirection::Left => current_widget.handle_widget_selection_left(),
@@ -1408,16 +1439,18 @@ pub fn move_widget_selection(
MovementDirection::Down => current_widget.handle_widget_selection_down(),
};
+ // TODO: Do testing.
+
match handled {
SelectionAction::Handled => {
// If it was handled by the widget, then we don't have to do anything - return the current one.
- current_widget_id
+ MoveWidgetResult::ForceRedraw(current_widget_id)
}
SelectionAction::NotHandled => {
/// Keeps traversing up the `layout_tree` until it hits a parent where `current_id` is a child and parent
/// is a [`LayoutNode::Row`], returning its parent's [`NodeId`] and the child's [`NodeId`] (in that order).
/// If this crawl fails (i.e. hits a root, it is an invalid tree for some reason), it returns [`None`].
- fn find_first_row(
+ fn find_parent_row(
layout_tree: &Arena, current_id: NodeId,
) -> Option<(NodeId, NodeId)> {
layout_tree
@@ -1430,7 +1463,7 @@ pub fn move_widget_selection(
})
.and_then(|(parent_id, parent_node)| match parent_node.get() {
LayoutNode::Row(_) => Some((parent_id, current_id)),
- LayoutNode::Col(_) => find_first_row(layout_tree, parent_id),
+ LayoutNode::Col(_) => find_parent_row(layout_tree, parent_id),
LayoutNode::Widget(_) => None,
})
}
@@ -1438,7 +1471,7 @@ pub fn move_widget_selection(
/// Keeps traversing up the `layout_tree` until it hits a parent where `current_id` is a child and parent
/// is a [`LayoutNode::Col`], returning its parent's [`NodeId`] and the child's [`NodeId`] (in that order).
/// If this crawl fails (i.e. hits a root, it is an invalid tree for some reason), it returns [`None`].
- fn find_first_col(
+ fn find_parent_col(
layout_tree: &Arena, current_id: NodeId,
) -> Option<(NodeId, NodeId)> {
layout_tree
@@ -1450,7 +1483,7 @@ pub fn move_widget_selection(
.map(|parent_node| (parent_id, parent_node))
})
.and_then(|(parent_id, parent_node)| match parent_node.get() {
- LayoutNode::Row(_) => find_first_col(layout_tree, parent_id),
+ LayoutNode::Row(_) => find_parent_col(layout_tree, parent_id),
LayoutNode::Col(_) => Some((parent_id, current_id)),
LayoutNode::Widget(_) => None,
})
@@ -1461,21 +1494,19 @@ pub fn move_widget_selection(
if let Some(current_node) = layout_tree.get(current_id) {
match current_node.get() {
LayoutNode::Row(RowLayout {
- last_selected_index,
+ last_selected,
parent_rule: _,
bound: _,
})
| LayoutNode::Col(ColLayout {
- last_selected_index,
+ last_selected,
parent_rule: _,
bound: _,
}) => {
- if let Some(next_child) =
- current_id.children(layout_tree).nth(*last_selected_index)
- {
+ if let Some(next_child) = *last_selected {
descend_to_leaf(layout_tree, next_child)
} else {
- current_id
+ current_node.first_child().unwrap_or(current_id)
}
}
LayoutNode::Widget(_) => {
@@ -1493,7 +1524,7 @@ pub fn move_widget_selection(
// on the tree layout to help us decide where to go.
// Movement logic is inspired by i3. When we enter a new column/row, we go to the *last* selected
// element; if we can't, go to the nearest one.
- match direction {
+ let proposed_id = match direction {
MovementDirection::Left => {
// When we move "left":
// 1. Look for the parent of the current widget.
@@ -1513,7 +1544,8 @@ pub fn move_widget_selection(
fn find_left(
layout_tree: &mut Arena, current_id: NodeId,
) -> NodeId {
- if let Some((parent_id, child_id)) = find_first_row(layout_tree, current_id)
+ if let Some((parent_id, child_id)) =
+ find_parent_row(layout_tree, current_id)
{
if let Some(prev_sibling) =
child_id.preceding_siblings(layout_tree).nth(1)
@@ -1521,16 +1553,17 @@ pub fn move_widget_selection(
// Subtract one from the currently selected index...
if let Some(parent) = layout_tree.get_mut(parent_id) {
if let LayoutNode::Row(row) = parent.get_mut() {
- row.last_selected_index =
- row.last_selected_index.saturating_sub(1);
+ row.last_selected = Some(prev_sibling);
}
}
// Now descend downwards!
descend_to_leaf(layout_tree, prev_sibling)
- } else {
+ } else if parent_id != current_id {
// Darn, we can't go further back! Recurse on this ID.
- find_left(layout_tree, child_id)
+ find_left(layout_tree, parent_id)
+ } else {
+ current_id
}
} else {
// Failed, just return the current ID.
@@ -1546,23 +1579,26 @@ pub fn move_widget_selection(
fn find_right(
layout_tree: &mut Arena, current_id: NodeId,
) -> NodeId {
- if let Some((parent_id, child_id)) = find_first_row(layout_tree, current_id)
+ if let Some((parent_id, child_id)) =
+ find_parent_row(layout_tree, current_id)
{
- if let Some(prev_sibling) =
+ if let Some(following_sibling) =
child_id.following_siblings(layout_tree).nth(1)
{
// Add one to the currently selected index...
if let Some(parent) = layout_tree.get_mut(parent_id) {
if let LayoutNode::Row(row) = parent.get_mut() {
- row.last_selected_index += 1;
+ row.last_selected = Some(following_sibling);
}
}
// Now descend downwards!
- descend_to_leaf(layout_tree, prev_sibling)
- } else {
+ descend_to_leaf(layout_tree, following_sibling)
+ } else if parent_id != current_id {
// Darn, we can't go further back! Recurse on this ID.
- find_right(layout_tree, child_id)
+ find_right(layout_tree, parent_id)
+ } else {
+ current_id
}
} else {
// Failed, just return the current ID.
@@ -1578,7 +1614,8 @@ pub fn move_widget_selection(
fn find_above(
layout_tree: &mut Arena, current_id: NodeId,
) -> NodeId {
- if let Some((parent_id, child_id)) = find_first_col(layout_tree, current_id)
+ if let Some((parent_id, child_id)) =
+ find_parent_col(layout_tree, current_id)
{
if let Some(prev_sibling) =
child_id.preceding_siblings(layout_tree).nth(1)
@@ -1586,16 +1623,17 @@ pub fn move_widget_selection(
// Subtract one from the currently selected index...
if let Some(parent) = layout_tree.get_mut(parent_id) {
if let LayoutNode::Col(row) = parent.get_mut() {
- row.last_selected_index =
- row.last_selected_index.saturating_sub(1);
+ row.last_selected = Some(prev_sibling);
}
}
// Now descend downwards!
descend_to_leaf(layout_tree, prev_sibling)
- } else {
+ } else if parent_id != current_id {
// Darn, we can't go further back! Recurse on this ID.
- find_above(layout_tree, child_id)
+ find_above(layout_tree, parent_id)
+ } else {
+ current_id
}
} else {
// Failed, just return the current ID.
@@ -1611,23 +1649,26 @@ pub fn move_widget_selection(
fn find_below(
layout_tree: &mut Arena, current_id: NodeId,
) -> NodeId {
- if let Some((parent_id, child_id)) = find_first_col(layout_tree, current_id)
+ if let Some((parent_id, child_id)) =
+ find_parent_col(layout_tree, current_id)
{
- if let Some(prev_sibling) =
+ if let Some(following_sibling) =
child_id.following_siblings(layout_tree).nth(1)
{
// Add one to the currently selected index...
if let Some(parent) = layout_tree.get_mut(parent_id) {
if let LayoutNode::Col(row) = parent.get_mut() {
- row.last_selected_index += 1;
+ row.last_selected = Some(following_sibling);
}
}
// Now descend downwards!
- descend_to_leaf(layout_tree, prev_sibling)
- } else {
+ descend_to_leaf(layout_tree, following_sibling)
+ } else if parent_id != current_id {
// Darn, we can't go further back! Recurse on this ID.
- find_below(layout_tree, child_id)
+ find_below(layout_tree, parent_id)
+ } else {
+ current_id
}
} else {
// Failed, just return the current ID.
@@ -1636,6 +1677,12 @@ pub fn move_widget_selection(
}
find_below(layout_tree, current_widget_id)
}
+ };
+
+ if let Some(LayoutNode::Widget(_)) = layout_tree.get(proposed_id).map(|n| n.get()) {
+ MoveWidgetResult::NodeId(proposed_id)
+ } else {
+ MoveWidgetResult::NodeId(current_widget_id)
}
}
}
diff --git a/src/app/widgets.rs b/src/app/widgets.rs
index cac6a032..ffc08cd6 100644
--- a/src/app/widgets.rs
+++ b/src/app/widgets.rs
@@ -149,6 +149,9 @@ pub trait Widget {
fn selectable_type(&self) -> SelectableType {
SelectableType::Selectable
}
+
+ /// Resets state in a [`Widget`]; used when a reset signal is given. The default implementation does nothing.
+ fn reset(&mut self) {}
}
/// Whether a widget can be selected, not selected, or redirected upon selection.
diff --git a/src/app/widgets/bottom_widgets/basic_mem.rs b/src/app/widgets/bottom_widgets/basic_mem.rs
index 2f33183f..d70d0e04 100644
--- a/src/app/widgets/bottom_widgets/basic_mem.rs
+++ b/src/app/widgets/bottom_widgets/basic_mem.rs
@@ -129,7 +129,7 @@ impl Widget for BasicMem {
// TODO: [Data update optimization] Probably should just make another function altogether for basic mode.
self.mem_data = if let (Some(data), Some((_, fraction))) = (
- convert_mem_data_points(data_collection, false).last(),
+ convert_mem_data_points(data_collection).last(),
memory_labels,
) {
(
@@ -141,7 +141,7 @@ impl Widget for BasicMem {
(0.0, "0.0B/0.0B".to_string(), "0%".to_string())
};
self.swap_data = if let (Some(data), Some((_, fraction))) = (
- convert_swap_data_points(data_collection, false).last(),
+ convert_swap_data_points(data_collection).last(),
swap_labels,
) {
Some((
diff --git a/src/app/widgets/bottom_widgets/basic_net.rs b/src/app/widgets/bottom_widgets/basic_net.rs
index bdb15554..a739fd25 100644
--- a/src/app/widgets/bottom_widgets/basic_net.rs
+++ b/src/app/widgets/bottom_widgets/basic_net.rs
@@ -109,7 +109,6 @@ impl Widget for BasicNet {
fn update_data(&mut self, data_collection: &DataCollection) {
let network_data = convert_network_data_points(
data_collection,
- false, // TODO: I think the is_frozen here is also useless; see mem and cpu
true,
&AxisScaling::Linear,
&self.unit_type,
diff --git a/src/app/widgets/bottom_widgets/cpu.rs b/src/app/widgets/bottom_widgets/cpu.rs
index 0b5cbae2..9397a7e1 100644
--- a/src/app/widgets/bottom_widgets/cpu.rs
+++ b/src/app/widgets/bottom_widgets/cpu.rs
@@ -9,7 +9,9 @@ use tui::{
use crate::{
app::{
- event::WidgetEventResult, text_table::SimpleColumn, time_graph::TimeGraphData,
+ event::{SelectionAction, WidgetEventResult},
+ text_table::SimpleColumn,
+ time_graph::TimeGraphData,
AppConfigFields, AppScrollWidgetState, CanvasTableWidthState, Component, DataCollection,
TextTable, TimeGraph, Widget,
},
@@ -47,7 +49,6 @@ impl CpuState {
enum CpuGraphSelection {
Graph,
Legend,
- None,
}
/// Whether the [`CpuGraph`]'s legend is placed on the left or right.
@@ -95,7 +96,7 @@ impl CpuGraph {
legend_position,
showing_avg,
bounds: Rect::default(),
- selected: CpuGraphSelection::None,
+ selected: CpuGraphSelection::Graph,
display_data: Default::default(),
load_avg_data: [0.0; 3],
width: LayoutRule::default(),
@@ -121,7 +122,6 @@ impl Component for CpuGraph {
match self.selected {
CpuGraphSelection::Graph => self.graph.handle_key_event(event),
CpuGraphSelection::Legend => self.legend.handle_key_event(event),
- CpuGraphSelection::None => WidgetEventResult::NoRedraw,
}
}
@@ -190,6 +190,55 @@ impl Widget for CpuGraph {
CpuGraphLegendPosition::Right => (split_area[0], split_area[1]),
};
+ let legend_block = self
+ .block()
+ .selected(selected && matches!(&self.selected, CpuGraphSelection::Legend))
+ .expanded(expanded)
+ .hide_title(true);
+
+ let legend_data = self
+ .display_data
+ .iter()
+ .enumerate()
+ .map(|(cpu_index, core_data)| {
+ let style = Some(if cpu_index == 0 {
+ painter.colours.all_colour_style
+ } else if self.showing_avg && cpu_index == 1 {
+ painter.colours.avg_colour_style
+ } else {
+ let cpu_style_index = if self.showing_avg {
+ // No underflow should occur, as if cpu_index was
+ // 1 and avg is showing, it's caught by the above case!
+ cpu_index - 2
+ } else {
+ cpu_index - 1
+ };
+ painter.colours.cpu_colour_styles
+ [cpu_style_index % painter.colours.cpu_colour_styles.len()]
+ });
+
+ vec![
+ (
+ core_data.cpu_name.clone().into(),
+ Some(core_data.short_cpu_name.clone().into()),
+ style,
+ ),
+ (core_data.legend_value.clone().into(), None, style),
+ ]
+ })
+ .collect::>();
+
+ // TODO: You MUST draw the table first, otherwise the index may mismatch after a reset. This is a bad gotcha - we should look into auto-updating the table's length!
+ self.legend.draw_tui_table(
+ painter,
+ f,
+ &legend_data,
+ legend_block,
+ legend_block_area,
+ true,
+ false,
+ );
+
const Y_BOUNDS: [f64; 2] = [0.0, 100.5];
let y_bound_labels: [Cow<'static, str>; 2] = ["0%".into(), "100%".into()];
@@ -243,59 +292,10 @@ impl Widget for CpuGraph {
graph_block,
graph_block_area,
);
-
- let legend_block = self
- .block()
- .selected(selected && matches!(&self.selected, CpuGraphSelection::Legend))
- .expanded(expanded)
- .hide_title(true);
-
- let legend_data = self
- .display_data
- .iter()
- .enumerate()
- .map(|(cpu_index, core_data)| {
- let style = Some(if cpu_index == 0 {
- painter.colours.all_colour_style
- } else if self.showing_avg && cpu_index == 1 {
- painter.colours.avg_colour_style
- } else {
- let cpu_style_index = if self.showing_avg {
- // No underflow should occur, as if cpu_index was
- // 1 and avg is showing, it's caught by the above case!
- cpu_index - 2
- } else {
- cpu_index - 1
- };
- painter.colours.cpu_colour_styles
- [cpu_style_index % painter.colours.cpu_colour_styles.len()]
- });
-
- vec![
- (
- core_data.cpu_name.clone().into(),
- Some(core_data.short_cpu_name.clone().into()),
- style,
- ),
- (core_data.legend_value.clone().into(), None, style),
- ]
- })
- .collect::>();
-
- self.legend.draw_tui_table(
- painter,
- f,
- &legend_data,
- legend_block,
- legend_block_area,
- true,
- false,
- );
}
fn update_data(&mut self, data_collection: &DataCollection) {
- // TODO: *Maybe* look into only taking in enough data for the current retention? Though this isn't great, it means you have to be like process with the whole updating thing.
- convert_cpu_data_points(data_collection, &mut self.display_data, false); // TODO: Again, the "is_frozen" is probably useless
+ convert_cpu_data_points(data_collection, &mut self.display_data);
self.load_avg_data = data_collection.load_avg_harvest;
}
@@ -306,4 +306,46 @@ impl Widget for CpuGraph {
fn height(&self) -> LayoutRule {
self.height
}
+
+ fn handle_widget_selection_left(&mut self) -> SelectionAction {
+ match self.legend_position {
+ CpuGraphLegendPosition::Left => {
+ if let CpuGraphSelection::Graph = self.selected {
+ self.selected = CpuGraphSelection::Legend;
+ SelectionAction::Handled
+ } else {
+ SelectionAction::NotHandled
+ }
+ }
+ CpuGraphLegendPosition::Right => {
+ if let CpuGraphSelection::Legend = self.selected {
+ self.selected = CpuGraphSelection::Graph;
+ SelectionAction::Handled
+ } else {
+ SelectionAction::NotHandled
+ }
+ }
+ }
+ }
+
+ fn handle_widget_selection_right(&mut self) -> SelectionAction {
+ match self.legend_position {
+ CpuGraphLegendPosition::Left => {
+ if let CpuGraphSelection::Legend = self.selected {
+ self.selected = CpuGraphSelection::Graph;
+ SelectionAction::Handled
+ } else {
+ SelectionAction::NotHandled
+ }
+ }
+ CpuGraphLegendPosition::Right => {
+ if let CpuGraphSelection::Graph = self.selected {
+ self.selected = CpuGraphSelection::Legend;
+ SelectionAction::Handled
+ } else {
+ SelectionAction::NotHandled
+ }
+ }
+ }
+ }
}
diff --git a/src/app/widgets/bottom_widgets/mem.rs b/src/app/widgets/bottom_widgets/mem.rs
index 1531290b..11880421 100644
--- a/src/app/widgets/bottom_widgets/mem.rs
+++ b/src/app/widgets/bottom_widgets/mem.rs
@@ -129,8 +129,8 @@ impl Widget for MemGraph {
}
fn update_data(&mut self, data_collection: &DataCollection) {
- self.mem_data = convert_mem_data_points(data_collection, false); // TODO: I think the "is_frozen" part is useless... it's always false now.
- self.swap_data = convert_swap_data_points(data_collection, false);
+ self.mem_data = convert_mem_data_points(data_collection);
+ self.swap_data = convert_swap_data_points(data_collection);
let (memory_labels, swap_labels) = convert_mem_labels(data_collection);
self.mem_labels = memory_labels;
diff --git a/src/app/widgets/bottom_widgets/net.rs b/src/app/widgets/bottom_widgets/net.rs
index d9c0b799..b02b957f 100644
--- a/src/app/widgets/bottom_widgets/net.rs
+++ b/src/app/widgets/bottom_widgets/net.rs
@@ -568,7 +568,6 @@ impl Widget for NetGraph {
fn update_data(&mut self, data_collection: &DataCollection) {
let network_data = convert_network_data_points(
data_collection,
- false, // TODO: I think the is_frozen here is also useless; see mem and cpu
false,
&self.scale_type,
&self.unit_type,
@@ -709,7 +708,6 @@ impl Widget for OldNetGraph {
fn update_data(&mut self, data_collection: &DataCollection) {
let network_data = convert_network_data_points(
data_collection,
- false, // TODO: I think the is_frozen here is also useless; see mem and cpu
true,
&self.net_graph.scale_type,
&self.net_graph.unit_type,
diff --git a/src/app/widgets/bottom_widgets/process.rs b/src/app/widgets/bottom_widgets/process.rs
index 87177b22..eeff0b54 100644
--- a/src/app/widgets/bottom_widgets/process.rs
+++ b/src/app/widgets/bottom_widgets/process.rs
@@ -15,7 +15,7 @@ use tui::{
use crate::{
app::{
data_harvester::processes::ProcessHarvest,
- event::{MultiKey, MultiKeyResult, ReturnSignal, WidgetEventResult},
+ event::{MultiKey, MultiKeyResult, ReturnSignal, SelectionAction, WidgetEventResult},
query::*,
text_table::DesiredColumnWidth,
widgets::tui_stuff::BlockBuilder,
@@ -575,6 +575,7 @@ impl ProcState {
}
/// The currently selected part of a [`ProcessManager`]
+#[derive(PartialEq, Eq, Clone, Copy)]
enum ProcessManagerSelection {
Processes,
Sort,
@@ -775,6 +776,7 @@ pub struct ProcessManager {
dd_multi: MultiKey,
selected: ProcessManagerSelection,
+ prev_selected: ProcessManagerSelection,
in_tree_mode: bool,
show_sort: bool,
@@ -819,6 +821,7 @@ impl ProcessManager {
search_block_bounds: Rect::default(),
dd_multi: MultiKey::register(vec!['d', 'd']), // TODO: Maybe use something static...
selected: ProcessManagerSelection::Processes,
+ prev_selected: ProcessManagerSelection::Processes,
in_tree_mode: false,
show_sort: false,
show_search: false,
@@ -871,6 +874,7 @@ impl ProcessManager {
WidgetEventResult::NoRedraw
} else {
self.show_search = true;
+ self.prev_selected = self.selected;
self.selected = ProcessManagerSelection::Search;
WidgetEventResult::Redraw
}
@@ -883,6 +887,7 @@ impl ProcessManager {
self.sort_menu
.set_index(self.process_table.current_sorting_column_index());
self.show_sort = true;
+ self.prev_selected = self.selected;
self.selected = ProcessManagerSelection::Sort;
WidgetEventResult::Redraw
}
@@ -966,6 +971,7 @@ impl ProcessManager {
fn hide_sort(&mut self) {
self.show_sort = false;
if let ProcessManagerSelection::Sort = self.selected {
+ self.prev_selected = self.selected;
self.selected = ProcessManagerSelection::Processes;
}
}
@@ -973,6 +979,7 @@ impl ProcessManager {
fn hide_search(&mut self) {
self.show_search = false;
if let ProcessManagerSelection::Search = self.selected {
+ self.prev_selected = self.selected;
self.selected = ProcessManagerSelection::Processes;
}
}
@@ -1129,6 +1136,7 @@ impl Component for ProcessManager {
if let ProcessManagerSelection::Processes = self.selected {
self.process_table.handle_mouse_event(event)
} else {
+ self.prev_selected = self.selected;
self.selected = ProcessManagerSelection::Processes;
match self.process_table.handle_mouse_event(event) {
WidgetEventResult::Quit => WidgetEventResult::Quit,
@@ -1142,6 +1150,7 @@ impl Component for ProcessManager {
if let ProcessManagerSelection::Sort = self.selected {
self.sort_menu.handle_mouse_event(event)
} else {
+ self.prev_selected = self.selected;
self.selected = ProcessManagerSelection::Sort;
self.sort_menu.handle_mouse_event(event);
WidgetEventResult::Redraw
@@ -1154,6 +1163,7 @@ impl Component for ProcessManager {
if let ProcessManagerSelection::Search = self.selected {
self.search_input.handle_mouse_event(event)
} else {
+ self.prev_selected = self.selected;
self.selected = ProcessManagerSelection::Search;
self.search_input.handle_mouse_event(event);
WidgetEventResult::Redraw
@@ -1261,9 +1271,11 @@ impl Widget for ProcessManager {
area
};
+ let process_selected =
+ selected && matches!(self.selected, ProcessManagerSelection::Processes);
let process_block = self
.block()
- .selected(selected && matches!(self.selected, ProcessManagerSelection::Processes))
+ .selected(process_selected)
.borders(self.block_border)
.expanded(expanded && !self.show_sort && !self.show_search);
@@ -1273,7 +1285,7 @@ impl Widget for ProcessManager {
&self.display_data,
process_block,
area,
- selected,
+ process_selected,
self.show_scroll_index,
);
}
@@ -1485,4 +1497,69 @@ impl Widget for ProcessManager {
fn height(&self) -> LayoutRule {
self.height
}
+
+ fn handle_widget_selection_left(&mut self) -> SelectionAction {
+ if self.show_sort {
+ if let ProcessManagerSelection::Processes = self.selected {
+ self.prev_selected = self.selected;
+ self.selected = ProcessManagerSelection::Sort;
+ SelectionAction::Handled
+ } else {
+ SelectionAction::NotHandled
+ }
+ } else {
+ SelectionAction::NotHandled
+ }
+ }
+
+ fn handle_widget_selection_right(&mut self) -> SelectionAction {
+ if self.show_sort {
+ if let ProcessManagerSelection::Sort = self.selected {
+ self.prev_selected = self.selected;
+ self.selected = ProcessManagerSelection::Processes;
+ SelectionAction::Handled
+ } else {
+ SelectionAction::NotHandled
+ }
+ } else {
+ SelectionAction::NotHandled
+ }
+ }
+
+ fn handle_widget_selection_up(&mut self) -> SelectionAction {
+ if self.show_search {
+ if let ProcessManagerSelection::Search = self.selected {
+ let prev = self.prev_selected;
+ self.prev_selected = self.selected;
+ if self.show_sort && prev == ProcessManagerSelection::Sort {
+ self.selected = ProcessManagerSelection::Sort;
+ } else {
+ self.selected = ProcessManagerSelection::Processes;
+ }
+ SelectionAction::Handled
+ } else {
+ SelectionAction::NotHandled
+ }
+ } else {
+ SelectionAction::NotHandled
+ }
+ }
+
+ fn handle_widget_selection_down(&mut self) -> SelectionAction {
+ if self.show_search {
+ if let ProcessManagerSelection::Processes = self.selected {
+ self.prev_selected = self.selected;
+ self.selected = ProcessManagerSelection::Search;
+ SelectionAction::Handled
+ } else if self.show_sort && self.selected == ProcessManagerSelection::Sort {
+ self.prev_selected = self.selected;
+ self.selected = ProcessManagerSelection::Search;
+ SelectionAction::Handled
+ } else {
+ SelectionAction::NotHandled
+ }
+ } else {
+ SelectionAction::NotHandled
+ }
+ }
}
diff --git a/src/bin/main.rs b/src/bin/main.rs
index 3c52a978..358b95be 100644
--- a/src/bin/main.rs
+++ b/src/bin/main.rs
@@ -51,7 +51,7 @@ fn main() -> Result<()> {
// Set up input handling
let (sender, receiver) = mpsc::channel(); // FIXME: Make this bounded, prevents overloading.
- let _input_thread = create_input_thread(sender.clone(), thread_termination_lock.clone());
+ let input_thread = create_input_thread(sender.clone(), thread_termination_lock.clone());
// Cleaning loop
// TODO: Probably worth spinning this off into an async thread or something...
@@ -102,6 +102,7 @@ fn main() -> Result<()> {
terminal.hide_cursor()?;
// Set panic hook
+ // TODO: Make this close all the child threads too!
panic::set_hook(Box::new(|info| panic_hook(info)));
// Set termination hook
@@ -135,6 +136,8 @@ fn main() -> Result<()> {
// I think doing it in this order is safe...
*thread_termination_lock.lock().unwrap() = true;
thread_termination_cvar.notify_all();
+
+ let _ = input_thread.join();
cleanup_terminal(&mut terminal)?;
Ok(())
diff --git a/src/canvas.rs b/src/canvas.rs
index ecf40633..0c91978d 100644
--- a/src/canvas.rs
+++ b/src/canvas.rs
@@ -207,7 +207,7 @@ impl Painter {
&mut self, terminal: &mut Terminal, app_state: &mut app::AppState,
) -> error::Result<()> {
terminal.draw(|mut f| {
- let (draw_area, frozen_draw_loc) = if app_state.is_frozen {
+ let (draw_area, frozen_draw_loc) = if app_state.is_frozen() {
let split_loc = Layout::default()
.constraints([Constraint::Min(0), Constraint::Length(1)])
.split(f.size());
diff --git a/src/constants.rs b/src/constants.rs
index a95ef6db..636616c7 100644
--- a/src/constants.rs
+++ b/src/constants.rs
@@ -243,8 +243,8 @@ pub const GENERAL_HELP_TEXT: [&str; 30] = [
"1 - General",
"q, Ctrl-c Quit",
"Esc Close dialog windows, search, widgets, or exit expanded mode",
- "Ctrl-r Reset display and any collected data",
- "f Freeze/unfreeze updating with new data",
+ "Ctrl-r Resets any collected data",
+ "f Toggles freezing, which stops new data from being shown",
"Ctrl-Left, ",
"Shift-Left, Move widget selection left",
"H, A ",
diff --git a/src/data_conversion.rs b/src/data_conversion.rs
index d75d98cc..a201a3a8 100644
--- a/src/data_conversion.rs
+++ b/src/data_conversion.rs
@@ -189,17 +189,9 @@ pub fn convert_disk_row(current_data: &DataCollection) -> TextTableData {
}
pub fn convert_cpu_data_points(
- current_data: &DataCollection, existing_cpu_data: &mut Vec, is_frozen: bool,
+ current_data: &DataCollection, existing_cpu_data: &mut Vec,
) {
- let current_time = if is_frozen {
- if let Some(frozen_instant) = current_data.frozen_instant {
- frozen_instant
- } else {
- current_data.current_instant
- }
- } else {
- current_data.current_instant
- };
+ let current_time = current_data.current_instant;
// Initialize cpu_data_vector if the lengths don't match...
if let Some((_time, data)) = ¤t_data.timed_data_vec.last() {
@@ -250,34 +242,32 @@ pub fn convert_cpu_data_points(
cpu.legend_value = format!("{:.0}%", cpu_usage.round());
});
}
- }
- for (time, data) in ¤t_data.timed_data_vec {
- let time_from_start: f64 = (current_time.duration_since(*time).as_millis() as f64).floor();
+ for (time, data) in ¤t_data.timed_data_vec {
+ let time_from_start: f64 =
+ (current_time.duration_since(*time).as_millis() as f64).floor();
- for (itx, cpu) in data.cpu_data.iter().enumerate() {
- if let Some(cpu_data) = existing_cpu_data.get_mut(itx + 1) {
- cpu_data.cpu_data.push((-time_from_start, *cpu));
+ for (itx, cpu) in data.cpu_data.iter().enumerate() {
+ if let Some(cpu_data) = existing_cpu_data.get_mut(itx + 1) {
+ cpu_data.cpu_data.push((-time_from_start, *cpu));
+ }
+ }
+
+ if *time == current_time {
+ break;
}
}
-
- if *time == current_time {
- break;
+ } else {
+ // No data, clear if non-empty. This is usually the case after a reset.
+ if !existing_cpu_data.is_empty() {
+ *existing_cpu_data = vec![];
}
}
}
-pub fn convert_mem_data_points(current_data: &DataCollection, is_frozen: bool) -> Vec {
+pub fn convert_mem_data_points(current_data: &DataCollection) -> Vec {
let mut result: Vec = Vec::new();
- let current_time = if is_frozen {
- if let Some(frozen_instant) = current_data.frozen_instant {
- frozen_instant
- } else {
- current_data.current_instant
- }
- } else {
- current_data.current_instant
- };
+ let current_time = current_data.current_instant;
for (time, data) in ¤t_data.timed_data_vec {
if let Some(mem_data) = data.mem_data {
@@ -293,17 +283,9 @@ pub fn convert_mem_data_points(current_data: &DataCollection, is_frozen: bool) -
result
}
-pub fn convert_swap_data_points(current_data: &DataCollection, is_frozen: bool) -> Vec {
+pub fn convert_swap_data_points(current_data: &DataCollection) -> Vec {
let mut result: Vec = Vec::new();
- let current_time = if is_frozen {
- if let Some(frozen_instant) = current_data.frozen_instant {
- frozen_instant
- } else {
- current_data.current_instant
- }
- } else {
- current_data.current_instant
- };
+ let current_time = current_data.current_instant;
for (time, data) in ¤t_data.timed_data_vec {
if let Some(swap_data) = data.swap_data {
@@ -391,21 +373,13 @@ pub fn convert_mem_labels(
}
pub fn get_rx_tx_data_points(
- current_data: &DataCollection, is_frozen: bool, network_scale_type: &AxisScaling,
- network_unit_type: &DataUnit, network_use_binary_prefix: bool,
+ current_data: &DataCollection, network_scale_type: &AxisScaling, network_unit_type: &DataUnit,
+ network_use_binary_prefix: bool,
) -> (Vec, Vec) {
let mut rx: Vec = Vec::new();
let mut tx: Vec = Vec::new();
- let current_time = if is_frozen {
- if let Some(frozen_instant) = current_data.frozen_instant {
- frozen_instant
- } else {
- current_data.current_instant
- }
- } else {
- current_data.current_instant
- };
+ let current_time = current_data.current_instant;
for (time, data) in ¤t_data.timed_data_vec {
let time_from_start: f64 = (current_time.duration_since(*time).as_millis() as f64).floor();
@@ -446,13 +420,11 @@ pub fn get_rx_tx_data_points(
}
pub fn convert_network_data_points(
- current_data: &DataCollection, is_frozen: bool, need_four_points: bool,
- network_scale_type: &AxisScaling, network_unit_type: &DataUnit,
- network_use_binary_prefix: bool,
+ current_data: &DataCollection, need_four_points: bool, network_scale_type: &AxisScaling,
+ network_unit_type: &DataUnit, network_use_binary_prefix: bool,
) -> ConvertedNetworkData {
let (rx, tx) = get_rx_tx_data_points(
current_data,
- is_frozen,
network_scale_type,
network_unit_type,
network_use_binary_prefix,
diff --git a/src/lib.rs b/src/lib.rs
index 4716881c..05718df6 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -21,7 +21,7 @@ use std::{
use crossterm::{
event::{
- read, DisableMouseCapture, Event, KeyCode, KeyEvent, KeyModifiers, MouseEvent,
+ poll, read, DisableMouseCapture, Event, KeyCode, KeyEvent, KeyModifiers, MouseEvent,
MouseEventKind,
},
execute,
@@ -251,16 +251,11 @@ pub fn cleanup_terminal(
)?;
terminal.show_cursor()?;
- // if is_debug {
- // let mut tmp_dir = std::env::temp_dir();
- // tmp_dir.push("bottom_debug.log");
- // println!("Your debug file is located at {:?}", tmp_dir.as_os_str());
- // }
-
Ok(())
}
/// Based on https://github.com/Rigellute/spotify-tui/blob/master/src/main.rs
+#[allow(clippy::mutex_atomic)]
pub fn panic_hook(panic_info: &PanicInfo<'_>) {
let mut stdout = stdout();
@@ -307,32 +302,37 @@ pub fn create_input_thread(
}
}
- if let Ok(event) = read() {
- match event {
- Event::Key(event) => {
- if Instant::now().duration_since(keyboard_timer).as_millis() >= 20 {
- if sender.send(BottomEvent::KeyInput(event)).is_err() {
- break;
+ if let Ok(poll) = poll(Duration::from_millis(20)) {
+ if poll {
+ if let Ok(event) = read() {
+ match event {
+ Event::Key(event) => {
+ if Instant::now().duration_since(keyboard_timer).as_millis() >= 20 {
+ if sender.send(BottomEvent::KeyInput(event)).is_err() {
+ break;
+ }
+ keyboard_timer = Instant::now();
+ }
}
- keyboard_timer = Instant::now();
- }
- }
- Event::Mouse(event) => match &event.kind {
- MouseEventKind::Drag(_) => {}
- MouseEventKind::Moved => {}
- _ => {
- if Instant::now().duration_since(mouse_timer).as_millis() >= 20 {
- if sender.send(BottomEvent::MouseInput(event)).is_err() {
+ Event::Mouse(event) => match &event.kind {
+ MouseEventKind::Drag(_) => {}
+ MouseEventKind::Moved => {}
+ _ => {
+ if Instant::now().duration_since(mouse_timer).as_millis() >= 20
+ {
+ if sender.send(BottomEvent::MouseInput(event)).is_err() {
+ break;
+ }
+ mouse_timer = Instant::now();
+ }
+ }
+ },
+ Event::Resize(width, height) => {
+ if sender.send(BottomEvent::Resize { width, height }).is_err() {
break;
}
- mouse_timer = Instant::now();
}
}
- },
- Event::Resize(width, height) => {
- if sender.send(BottomEvent::Resize { width, height }).is_err() {
- break;
- }
}
}
}