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;
/// 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 ProcWidgetState {
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 ProcWidgetState {
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