summaryrefslogtreecommitdiffstats
path: root/src/app/widgets
diff options
context:
space:
mode:
Diffstat (limited to 'src/app/widgets')
-rw-r--r--src/app/widgets/battery_widget.rs5
-rw-r--r--src/app/widgets/cpu_graph.rs204
-rw-r--r--src/app/widgets/disk_table.rs197
-rw-r--r--src/app/widgets/mem_graph.rs15
-rw-r--r--src/app/widgets/net_graph.rs15
-rw-r--r--src/app/widgets/process_table.rs958
-rw-r--r--src/app/widgets/process_table/proc_widget_column.rs123
-rw-r--r--src/app/widgets/process_table/proc_widget_data.rs282
-rw-r--r--src/app/widgets/process_table/sort_table.rs42
-rw-r--r--src/app/widgets/temperature_table.rs138
10 files changed, 0 insertions, 1979 deletions
diff --git a/src/app/widgets/battery_widget.rs b/src/app/widgets/battery_widget.rs
deleted file mode 100644
index 68bbca79..00000000
--- a/src/app/widgets/battery_widget.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-#[derive(Default)]
-pub struct BatteryWidgetState {
- pub currently_selected_battery_index: usize,
- pub tab_click_locs: Option<Vec<((u16, u16), (u16, u16))>>,
-}
diff --git a/src/app/widgets/cpu_graph.rs b/src/app/widgets/cpu_graph.rs
deleted file mode 100644
index 7f0c1495..00000000
--- a/src/app/widgets/cpu_graph.rs
+++ /dev/null
@@ -1,204 +0,0 @@
-use std::{borrow::Cow, time::Instant};
-
-use concat_string::concat_string;
-use tui::{style::Style, text::Text, widgets::Row};
-
-use crate::{
- app::{data_harvester::cpu::CpuDataType, AppConfigFields},
- canvas::{canvas_colours::CanvasColours, Painter},
- components::data_table::{
- Column, ColumnHeader, DataTable, DataTableColumn, DataTableProps, DataTableStyling,
- DataToCell,
- },
- data_conversion::CpuWidgetData,
- utils::gen_util::truncate_text,
-};
-
-#[derive(Default)]
-pub struct CpuWidgetStyling {
- pub all: Style,
- pub avg: Style,
- pub entries: Vec<Style>,
-}
-
-impl CpuWidgetStyling {
- fn from_colours(colours: &CanvasColours) -> Self {
- let entries = if colours.cpu_colour_styles.is_empty() {
- vec![Style::default()]
- } else {
- colours.cpu_colour_styles.clone()
- };
-
- Self {
- all: colours.all_colour_style,
- avg: colours.avg_colour_style,
- entries,
- }
- }
-}
-
-pub enum CpuWidgetColumn {
- CPU,
- Use,
-}
-
-impl ColumnHeader for CpuWidgetColumn {
- fn text(&self) -> Cow<'static, str> {
- match self {
- CpuWidgetColumn::CPU => "CPU".into(),
- CpuWidgetColumn::Use => "Use%".into(),
- }
- }
-}
-
-pub enum CpuWidgetTableData {
- All,
- Entry {
- data_type: CpuDataType,
- last_entry: f64,
- },
-}
-
-impl CpuWidgetTableData {
- pub fn from_cpu_widget_data(data: &CpuWidgetData) -> CpuWidgetTableData {
- match data {
- CpuWidgetData::All => CpuWidgetTableData::All,
- CpuWidgetData::Entry {
- data_type,
- data: _,
- last_entry,
- } => CpuWidgetTableData::Entry {
- data_type: *data_type,
- last_entry: *last_entry,
- },
- }
- }
-}
-
-impl DataToCell<CpuWidgetColumn> for CpuWidgetTableData {
- fn to_cell<'a>(&'a self, column: &CpuWidgetColumn, calculated_width: u16) -> Option<Text<'a>> {
- const CPU_HIDE_BREAKPOINT: u16 = 5;
-
- // This is a bit of a hack, but apparently we can avoid having to do any fancy checks
- // of showing the "All" on a specific column if the other is hidden by just always
- // showing it on the CPU (first) column - if there isn't room for it, it will just collapse
- // down.
- //
- // This is the same for the use percentages - we just *always* show them, and *always* hide the CPU column if
- // it is too small.
- match &self {
- CpuWidgetTableData::All => match column {
- CpuWidgetColumn::CPU => Some(truncate_text("All", calculated_width)),
- CpuWidgetColumn::Use => None,
- },
- CpuWidgetTableData::Entry {
- data_type,
- last_entry,
- } => match column {
- CpuWidgetColumn::CPU => {
- if calculated_width == 0 {
- None
- } else {
- match data_type {
- CpuDataType::Avg => Some(truncate_text("AVG", calculated_width)),
- CpuDataType::Cpu(index) => {
- let index_str = index.to_string();
- let text = if calculated_width < CPU_HIDE_BREAKPOINT {
- truncate_text(&index_str, calculated_width)
- } else {
- truncate_text(
- &concat_string!("CPU", index_str),
- calculated_width,
- )
- };
-
- Some(text)
- }
- }
- }
- }
- CpuWidgetColumn::Use => Some(truncate_text(
- &format!("{:.0}%", last_entry.round()),
- calculated_width,
- )),
- },
- }
- }
-
- #[inline(always)]
- fn style_row<'a>(&self, row: Row<'a>, painter: &Painter) -> Row<'a> {
- let style = match self {
- CpuWidgetTableData::All => painter.colours.all_colour_style,
- CpuWidgetTableData::Entry {
- data_type,
- last_entry: _,
- } => match data_type {
- CpuDataType::Avg => painter.colours.avg_colour_style,
- CpuDataType::Cpu(index) => {
- painter.colours.cpu_colour_styles
- [index % painter.colours.cpu_colour_styles.len()]
- }
- },
- };
-
- row.style(style)
- }
-
- fn column_widths<C: DataTableColumn<CpuWidgetColumn>>(
- _data: &[Self], _columns: &[C],
- ) -> Vec<u16>
- where
- Self: Sized,
- {
- vec![1, 3]
- }
-}
-
-pub struct CpuWidgetState {
- pub current_display_time: u64,
- pub is_legend_hidden: bool,
- pub show_avg: bool,
- pub autohide_timer: Option<Instant>,
- pub table: DataTable<CpuWidgetTableData, CpuWidgetColumn>,
- pub styling: CpuWidgetStyling,
-}
-
-impl CpuWidgetState {
- pub fn new(
- config: &AppConfigFields, current_display_time: u64, autohide_timer: Option<Instant>,
- colours: &CanvasColours,
- ) -> Self {
- const COLUMNS: [Column<CpuWidgetColumn>; 2] = [
- Column::soft(CpuWidgetColumn::CPU, Some(0.5)),
- Column::soft(CpuWidgetColumn::Use, Some(0.5)),
- ];
-
- let props = DataTableProps {
- title: None,
- table_gap: config.table_gap,
- left_to_right: false,
- is_basic: false,
- show_table_scroll_position: false, // TODO: Should this be possible?
- show_current_entry_when_unfocused: true,
- };
-
- let styling = DataTableStyling::from_colours(colours);
-
- CpuWidgetState {
- current_display_time,
- is_legend_hidden: false,
- show_avg: config.show_average_cpu,
- autohide_timer,
- table: DataTable::new(COLUMNS, props, styling),
- styling: CpuWidgetStyling::from_colours(colours),
- }
- }
-
- pub fn update_table(&mut self, data: &[CpuWidgetData]) {
- self.table.set_data(
- data.iter()
- .map(CpuWidgetTableData::from_cpu_widget_data)
- .collect(),
- );
- }
-}
diff --git a/src/app/widgets/disk_table.rs b/src/app/widgets/disk_table.rs
deleted file mode 100644
index 1d4cc604..00000000
--- a/src/app/widgets/disk_table.rs
+++ /dev/null
@@ -1,197 +0,0 @@
-use std::{borrow::Cow, cmp::max};
-
-use kstring::KString;
-use tui::text::Text;
-
-use crate::{
- app::AppConfigFields,
- canvas::canvas_colours::CanvasColours,
- components::data_table::{
- ColumnHeader, DataTableColumn, DataTableProps, DataTableStyling, DataToCell, SortColumn,
- SortDataTable, SortDataTableProps, SortOrder, SortsRow,
- },
- utils::gen_util::{get_decimal_bytes, sort_partial_fn, truncate_text},
-};
-
-#[derive(Clone, Debug)]
-pub struct DiskWidgetData {
- pub name: KString,
- pub mount_point: KString,
- pub free_bytes: Option<u64>,
- pub used_bytes: Option<u64>,
- pub total_bytes: Option<u64>,
- pub io_read: KString,
- pub io_write: KString,
-}
-
-impl DiskWidgetData {
- pub fn free_space(&self) -> KString {
- if let Some(free_bytes) = self.free_bytes {
- let converted_free_space = get_decimal_bytes(free_bytes);
- format!("{:.*}{}", 0, converted_free_space.0, converted_free_space.1).into()
- } else {
- "N/A".into()
- }
- }
-
- pub fn total_space(&self) -> KString {
- if let Some(total_bytes) = self.total_bytes {
- let converted_total_space = get_decimal_bytes(total_bytes);
- format!(
- "{:.*}{}",
- 0, converted_total_space.0, converted_total_space.1
- )
- .into()
- } else {
- "N/A".into()
- }
- }
-
- pub fn usage(&self) -> KString {
- if let (Some(used_bytes), Some(total_bytes)) = (self.used_bytes, self.total_bytes) {
- format!("{:.0}%", used_bytes as f64 / total_bytes as f64 * 100_f64).into()
- } else {
- "N/A".into()
- }
- }
-}
-
-pub enum DiskWidgetColumn {
- Disk,
- Mount,
- Used,
- Free,
- Total,
- IoRead,
- IoWrite,
-}
-
-impl ColumnHeader for DiskWidgetColumn {
- fn text(&self) -> Cow<'static, str> {
- match self {
- DiskWidgetColumn::Disk => "Disk(d)",
- DiskWidgetColumn::Mount => "Mount(m)",
- DiskWidgetColumn::Used => "Used(u)",
- DiskWidgetColumn::Free => "Free(n)",
- DiskWidgetColumn::Total => "Total(t)",
- DiskWidgetColumn::IoRead => "R/s(r)",
- DiskWidgetColumn::IoWrite => "W/s(w)",
- }
- .into()
- }
-}
-
-impl DataToCell<DiskWidgetColumn> for DiskWidgetData {
- fn to_cell<'a>(&'a self, column: &DiskWidgetColumn, calculated_width: u16) -> Option<Text<'a>> {
- let text = match column {
- DiskWidgetColumn::Disk => truncate_text(&self.name, calculated_width),
- DiskWidgetColumn::Mount => truncate_text(&self.mount_point, calculated_width),
- DiskWidgetColumn::Used => truncate_text(&self.usage(), calculated_width),
- DiskWidgetColumn::Free => truncate_text(&self.free_space(), calculated_width),
- DiskWidgetColumn::Total => truncate_text(&self.total_space(), calculated_width),
- DiskWidgetColumn::IoRead => truncate_text(&self.io_read, calculated_width),
- DiskWidgetColumn::IoWrite => truncate_text(&self.io_write, calculated_width),
- };
-
- Some(text)
- }
-
- fn column_widths<C: DataTableColumn<DiskWidgetColumn>>(
- data: &[Self], _columns: &[C],
- ) -> Vec<u16>
- where
- Self: Sized,
- {
- let mut widths = vec![0; 7];
-
- data.iter().for_each(|row| {
- widths[0] = max(widths[0], row.name.len() as u16);
- widths[1] = max(widths[1], row.mount_point.len() as u16);
- });
-
- widths
- }
-}
-
-pub struct DiskTableWidget {
- pub table: SortDataTable<DiskWidgetData, DiskWidgetColumn>,
- pub force_update_data: bool,
-}
-
-impl SortsRow for DiskWidgetColumn {
- type DataType = DiskWidgetData;
-
- fn sort_data(&self, data: &mut [Self::DataType], descending: bool) {
- match self {
- DiskWidgetColumn::Disk => {
- data.sort_by(|a, b| sort_partial_fn(descending)(&a.name, &b.name));
- }
- DiskWidgetColumn::Mount => {
- data.sort_by(|a, b| sort_partial_fn(descending)(&a.mount_point, &b.mount_point));
- }
- DiskWidgetColumn::Used => {
- data.sort_by(|a, b| sort_partial_fn(descending)(&a.used_bytes, &b.used_bytes));
- }
- DiskWidgetColumn::Free => {
- data.sort_by(|a, b| sort_partial_fn(descending)(&a.free_bytes, &b.free_bytes));
- }
- DiskWidgetColumn::Total => {
- data.sort_by(|a, b| sort_partial_fn(descending)(&a.total_bytes, &b.total_bytes));
- }
- DiskWidgetColumn::IoRead => {
- data.sort_by(|a, b| sort_partial_fn(descending)(&a.io_read, &b.io_read));
- }
- DiskWidgetColumn::IoWrite => {
- data.sort_by(|a, b| sort_partial_fn(descending)(&a.io_write, &b.io_write));
- }
- }
- }
-}
-
-impl DiskTableWidget {
- pub fn new(config: &AppConfigFields, colours: &CanvasColours) -> Self {
- let columns = [
- SortColumn::soft(DiskWidgetColumn::Disk, Some(0.2)),
- SortColumn::soft(DiskWidgetColumn::Mount, Some(0.2)),
- SortColumn::hard(DiskWidgetColumn::Used, 8).default_descending(),
- SortColumn::hard(DiskWidgetColumn::Free, 8).default_descending(),
- SortColumn::hard(DiskWidgetColumn::Total, 9).default_descending(),
- SortColumn::hard(DiskWidgetColumn::IoRead, 10).default_descending(),
- SortColumn::hard(DiskWidgetColumn::IoWrite, 11).default_descending(),
- ];
-
- let props = SortDataTableProps {
- inner: DataTableProps {
- title: Some(" Disks ".into()),
- table_gap: config.table_gap,
- left_to_right: true,
- is_basic: config.use_basic_mode,
- show_table_scroll_position: config.show_table_scroll_position,
- show_current_entry_when_unfocused: false,
- },
- sort_index: 0,
- order: SortOrder::Ascending,
- };
-
- let styling = DataTableStyling::from_colours(colours);
-
- Self {
- table: SortDataTable::new_sortable(columns, props, styling),
- force_update_data: false,
- }
- }
-
- /// Forces an update of the data stored.
- #[inline]
- pub fn force_data_update(&mut self) {
- self.force_update_data = true;
- }
-
- pub fn ingest_data(&mut self, data: &[DiskWidgetData]) {
- let mut data = data.to_vec();
- if let Some(column) = self.table.columns.get(self.table.sort_index()) {
- column.sort_by(&mut data, self.table.order());
- }
- self.table.set_data(data);
- }
-}
diff --git a/src/app/widgets/mem_graph.rs b/src/app/widgets/mem_graph.rs
deleted file mode 100644
index a86bd02a..00000000
--- a/src/app/widgets/mem_graph.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-use std::time::Instant;
-
-pub struct MemWidgetState {
- pub current_display_time: u64,
- pub autohide_timer: Option<Instant>,
-}
-
-impl MemWidgetState {
- pub fn init(current_display_time: u64, autohide_timer: Option<Instant>) -> Self {
- MemWidgetState {
- current_display_time,
- autohide_timer,
- }
- }
-}
diff --git a/src/app/widgets/net_graph.rs b/src/app/widgets/net_graph.rs
deleted file mode 100644
index e3526210..00000000
--- a/src/app/widgets/net_graph.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-use std::time::Instant;
-
-pub struct NetWidgetState {
- pub current_display_time: u64,
- pub autohide_timer: Option<Instant>,
-}
-
-impl NetWidgetState {
- pub fn init(current_display_time: u64, autohide_timer: Option<Instant>) -> Self {
- NetWidgetState {
- current_display_time,
- autohide_timer,
- }
- }
-}
diff --git a/src/app/widgets/process_table.rs b/src/app/widgets/process_table.rs
deleted file mode 100644
index 6cd1bda9..00000000
--- a/src/app/widgets/process_table.rs
+++ /dev/null
@@ -1,958 +0,0 @@
-use std::{borrow::Cow, collections::BTreeMap};
-
-use const_format::formatcp;
-use fxhash::{FxHashMap, FxHashSet};
-use itertools::Itertools;
-
-use crate::{
- app::{
- data_farmer::{DataCollection, ProcessData},
- data_harvester::processes::ProcessHarvest,
- query::*,
- AppConfigFields, AppSearchState,
- },
- canvas::canvas_colours::CanvasColours,
- components::data_table::{
- Column, ColumnHeader, ColumnWidthBounds, DataTable, DataTableColumn, DataTableProps,
- DataTableStyling, SortColumn, SortDataTable, SortDataTableProps, SortOrder, SortsRow,
- },
- Pid,
-};
-
-pub mod proc_widget_column;
-pub use proc_widget_column::*;
-
-pub mod proc_widget_data;
-pub use proc_widget_data::*;
-
-mod sort_table;
-use sort_table::SortTableColumn;
-use unicode_segmentation::GraphemeIncomplete;
-
-/// ProcessSearchState only deals with process' search's current settings and state.
-pub struct ProcessSearchState {
- pub search_state: AppSearchState,
- pub is_ignoring_case: bool,
- pub is_searching_whole_word: bool,
- pub is_searching_with_regex: bool,
-}
-
-impl Default for ProcessSearchState {
- fn default() -> Self {
- ProcessSearchState {
- search_state: AppSearchState::default(),
- is_ignoring_case: true,
- is_searching_whole_word: false,
- is_searching_with_regex: false,
- }
- }
-}
-
-impl ProcessSearchState {
- pub fn search_toggle_ignore_case(&mut self) {
- self.is_ignoring_case = !self.is_ignoring_case;
- }
-
- pub fn search_toggle_whole_word(&mut self) {
- self.is_searching_whole_word = !self.is_searching_whole_word;
- }
-
- pub fn search_toggle_regex(&mut self) {
- self.is_searching_with_regex = !self.is_searching_with_regex;
- }
-}
-
-#[derive(Clone, Debug)]
-pub enum ProcWidgetMode {
- Tree { collapsed_pids: FxHashSet<Pid> },
- Grouped,
- Normal,
-}
-
-type ProcessTable = SortDataTable<ProcWidgetData, ProcColumn>;
-type SortTable = DataTable<Cow<'static, str>, SortTableColumn>;
-type StringPidMap = FxHashMap<String, Vec<Pid>>;
-
-pub struct ProcWidget {
- pub mode: ProcWidgetMode,
-
- /// The state of the search box.
- pub proc_search: ProcessSearchState,
-
- /// The state of the main table.
- pub table: ProcessTable,
-
- /// The state of the togglable table that controls sorting.
- pub sort_table: SortTable,
-
- /// A name-to-pid mapping.
- pub id_pid_map: StringPidMap,
-
- pub is_sort_open: bool,
- pub force_rerender: bool,
- pub force_update_data: bool,
-}
-
-impl ProcWidget {
- pub const PID_OR_COUNT: usize = 0;
- pub const PROC_NAME_OR_CMD: usize = 1;
- pub const CPU: usize = 2;
- pub const MEM: usize = 3;
- pub const RPS: usize = 4;
- pub const WPS: usize = 5;
- pub const T_READ: usize = 6;
- pub const T_WRITE: usize = 7;
- #[cfg(target_family = "unix")]
- pub const USER: usize = 8;
- #[cfg(target_family = "unix")]
- pub const STATE: usize = 9;
- #[cfg(not(target_family = "unix"))]
- pub const STATE: usize = 8;
-
- fn new_sort_table(config: &AppConfigFields, colours: &CanvasColours) -> SortTable {
- const COLUMNS: [Column<SortTableColumn>; 1] = [Column::hard(SortTableColumn, 7)];
-
- let props = DataTableProps {
- title: None,
- table_gap: config.table_gap,
- left_to_right: true,
- is_basic: false,
- show_table_scroll_position: false,
- show_current_entry_when_unfocused: false,
- };
-
- let styling = DataTableStyling::from_colours(colours);
-
- DataTable::new(COLUMNS, props, styling)
- }
-
- fn new_process_table(
- config: &AppConfigFields, colours: &CanvasColours, mode: &ProcWidgetMode, is_count: bool,
- is_command: bool, show_memory_as_values: bool,
- ) -> ProcessTable {
- let (default_index, default_order) = if matches!(mode, ProcWidgetMode::Tree { .. }) {
- (Self::PID_OR_COUNT, SortOrder::Ascending)
- } else {
- (Self::CPU, SortOrder::Descending)
- };
-
- let columns = {
- use ProcColumn::*;
-
- let pid_or_count = SortColumn::new(if is_count { Count } else { Pid });
- let name_or_cmd = SortColumn::soft(if is_command { Command } else { Name }, Some(0.3));
- let cpu = SortColumn::new(CpuPercent).default_descending();
- let mem = SortColumn::new(if show_memory_as_values {
- MemoryVal
- } else {
- MemoryPercent
- })
- .default_descending();
- let rps = SortColumn::hard(ReadPerSecond, 8).default_descending();
- let wps = SortColumn::hard(WritePerSecond, 8).default_descending();
- let tr = SortColumn::hard(TotalRead, 8).default_descending();
- let tw = SortColumn::hard(TotalWrite, 8).default_descending();
- let state = SortColumn::hard(State, 7);
-
- vec![
- pid_or_count,
- name_or_cmd,
- cpu,
- mem,
- rps,
- wps,
- tr,
- tw,
- #[cfg(target_family = "unix")]
- SortColumn::soft(User, Some(0.05)),
- state,
- ]
- };
-
- let inner_props = DataTableProps {
- title: Some(" Processes ".into()),
- table_gap: config.table_gap,
- left_to_right: true,
- is_basic: config.use_basic_mode,
- show_table_scroll_position: config.show_table_scroll_position,
- show_current_entry_when_unfocused: false,
- };
- let props = SortDataTableProps {
- inner: inner_props,
- sort_index: default_index,
- order: default_order,
- };
-
- let styling = DataTableStyling::from_colours(colours);
-
- DataTable::new_sortable(columns, props, styling)
- }
-
- pub fn new(
- config: &AppConfigFields, mode: ProcWidgetMode, is_case_sensitive: bool,
- is_match_whole_word: bool, is_use_regex: bool, show_memory_as_values: bool,
- is_command: bool, colours: &CanvasColours,
- ) -> Self {
- let process_search_state = {
- let mut pss = ProcessSearchState::default();
-
- if is_case_sensitive {
- // By default it's off
- pss.search_toggle_ignore_case();
- }
- if is_match_whole_word {
- pss.search_toggle_whole_word();
- }
- if is_use_regex {
- pss.search_toggle_regex();
- }
-
- pss
- };
-
- let is_count = matches!(mode, ProcWidgetMode::Grouped);
- let sort_table = Self::new_sort_table(config, colours);
- let table = Self::new_process_table(
- config,
- colours,
- &mode,
- is_count,
- is_command,
- show_memory_as_values,
- );
-
- let id_pid_map = FxHashMap::default();
-
- let mut table = ProcWidget {
- proc_search: process_search_state,
- table,
- sort_table,
- id_pid_map,
- is_sort_open: false,
- mode,
- force_rerender: true,
- force_update_data: false,
- };
- table.sort_table.set_data(table.column_text());
-
- table
- }
-
- pub fn is_using_command(&self) -> bool {
- self.table
- .columns
- .get(ProcWidget::PROC_NAME_OR_CMD)
- .map(|col| matches!(col.inner(), ProcColumn::Command))
- .unwrap_or(false)
- }
-
- pub fn is_mem_percent(&self) -> bool {
- self.table
- .columns
- .get(ProcWidget::MEM)
- .map(|col| matches!(col.inner(), ProcColumn::MemoryPercent))
- .unwrap_or(false)
- }
-
- fn get_query(&self) -> &Option<Query> {
- if self.proc_search.search_state.is_invalid_or_blank_search() {
- &None
- } else {
- &self.proc_search.search_state.query
- }
- }
-
- /// This function *only* updates the displayed process data. If there is a need to update the actual *stored* data,
- /// call it before this function.
- pub fn ingest_data(&mut self, data_collection: &DataCollection) {
- let data = match &self.mode {
- ProcWidgetMode::Grouped | ProcWidgetMode::Normal => {
- self.get_normal_data(&data_collection.process_data.process_harvest)
- }
- ProcWidgetMode::Tree { collapsed_pids } => {
- self.get_tree_data(collapsed_pids, data_collection)
- }
- };
- self.table.set_data(data);
- }
-
- fn get_tree_data(
- &self, collapsed_pids: &FxHashSet<Pid>, data_collection: &DataCollection,
- ) -> Vec<ProcWidgetData> {
- const BRANCH_END: char = '└';
- const BRANCH_VERTICAL: char = '│';
- const BRANCH_SPLIT: char = '├';
- const BRANCH_HORIZONTAL: char = '─';
-
- let search_query = self.get_query();
- let is_using_command = self.is_using_command();
- let is_mem_percent = self.is_mem_percent();
-
- let ProcessData {
- process_harvest,
- process_parent_mapping,
- orphan_pids,
- ..
- } = &data_collection.process_data;
-
- let kept_pids = data_collection
- .process_data
- .process_harvest
- .iter()
- .map(|(pid, process)| {
- (
- *pid,
- search_query
- .as_ref()
- .map(|q| q.check(process, is_using_command))
- .unwrap_or(true),
- )
- })
- .collect::<FxHashMap<_, _>>();
-
- let filtered_tree = {
- let mut filtered_tree = FxHashMap::default();
-
- // We do a simple BFS traversal to build our filtered parent-to-tree mappings.
- let mut visited_pids = FxHashMap::default();
- let mut stack = orphan_pids
- .iter()
- .filter_map(|process| process_harvest.get(process))
- .collect_vec();
-
- while let Some(process) = stack.last() {
- let is_process_matching = *kept_pids.get(&process.pid).unwrap_or(&false);
-
- if let Some(children_pids) = process_parent_mapping.get(&process.pid) {
- if children_pids
- .iter()
- .all(|pid| visited_pids.contains_key(pid))
- {
- let shown_children = children_pids
- .iter()
- .filter(|pid| visited_pids.get(*pid).copied().unwrap_or(false))
- .collect_vec();
- let is_shown = is_process_matching || !shown_children.is_empty();
- visited_pids.insert(process.pid, is_shown);
-
- if is_shown {
- filtered_tree.insert(
- process.pid,
- shown_children
- .into_iter()
- .filter_map(|pid| {
- process_harvest.get(pid).map(|process| process.pid)
- })
- .collect_vec(),
- );
- }
-
- stack.pop();
- } else {
- children_pids
- .iter()
- .filter_map(|process| process_harvest.get(process))
- .rev()
- .for_each(|process| {
- stack.push(process);
- });
- }
- } else {
- if is_process_matching {
- filtered_tree.insert(process.pid, vec![]);
- }
-
- visited_pids.insert(process.pid, is_process_matching);
- stack.pop();
- }
- }
-
- filtered_tree
- };
-
- let mut data = vec![];
- let mut prefixes = vec![];
- let mut stack = orphan_pids
- .iter()
- .filter_map(|pid| {
- if filtered_tree.contains_key(pid) {
- process_harvest.get(pid).map(|process| {
- ProcWidgetData::from_data(process, is_using_command, is_mem_percent)
- })
- } else {
- None
- }
- })
- .collect_vec();
-
- stack.sort_unstable_by_key(|p| p.pid);
-
- let column = self.table.columns.get(self.table.sort_index()).unwrap();
- sort_skip_pid_asc(column.inner(), &mut stack, self.table.order());
-
- stack.reverse();
-
- let mut length_stack = vec![stack.len()];
-
- while let (Some(process), Some(siblings_left)) = (stack.pop(), length_stack.last_mut()) {
- *siblings_left -= 1;
-
- let disabled = !*kept_pids.get(&process.pid).unwrap_or(&false);
- let is_last = *siblings_left == 0;
-
- if collapsed_pids.contains(&process.pid) {
- let mut summed_process = process.clone();
-
- if let Some(children_pids) = filtered_tree.get(&process.pid) {
- let mut sum_queue = children_pids
- .iter()
- .filter_map(|child| {
- process_harvest.get(child).map(|p| {
- ProcWidgetData::from_data(p, is_using_command, is_mem_percent)
- })
- })
- .collect_vec();
-
- while let Some(process) = sum_queue.pop() {
- summed_process.add(&process);
-
- if let Some(pids) = filtered_tree.get(&process.pid) {
- sum_queue.extend(pids.iter().filter_map(|child| {
- process_harvest.get(child).map(|p| {
- ProcWidgetData::from_data(p, is_using_command, is_mem_percent)
- })
- }));
- }
- }
- }
-
- let prefix = if prefixes.is_empty() {
- "+ ".to_string()
- } else {
- format!(
- "{}{}{} + ",
- prefixes.join(""),
- if is_last { BRANCH_END } else { BRANCH_SPLIT },
- BRANCH_HORIZONTAL
- )
- };
-
- data.push(summed_process.prefix(Some(prefix)).disabled(disabled));
- } else {
- let prefix = if prefixes.is_empty() {
- String::default()
- } else {
- format!(
- "{}{}{} ",
- prefixes.join(""),
- if is_last { BRANCH_END } else { BRANCH_SPLIT },
- BRANCH_HORIZONTAL
- )
- };
- let pid = process.pid;
- data.push(process.prefix(Some(prefix)).disabled(disabled));
-
- if let Some(children_pids) = filtered_tree.get(&pid) {
- if prefixes.is_empty() {
- prefixes.push("");
- } else {
- prefixes.push(if is_last {
- " "
- } else {
- formatcp!("{} ", BRANCH_VERTICAL)
- });
- }
-
- let mut children = children_pids
- .iter()
- .filter_map(|child_pid| {
- process_harvest.get(child_pid).map(|p| {
- ProcWidgetData::from_data(p, is_using_command, is_mem_percent)
- })
- })
- .collect_vec();
-
- column.sort_by(&mut children, self.table.order().rev());
-
- length_stack.push(children.len());
- stack.extend(children);
- }
- }
-
- while let Some(children_left) = length_stack.last() {
- if *children_left == 0 {
- length_stack.pop();
- prefixes.pop();
- } else {
- break;
- }
-