mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-07-27 07:34:27 +02:00
other: deduplicate sorts, always sort proc by PID first (#898)
* other: deduplicate sorts, sort proc by PID by default * add proc test * remove sort in Windows * fix tree * fix test * Remove mut * Add comment on sorting processes
This commit is contained in:
parent
a07fa305fb
commit
f52b66a844
@ -13,10 +13,9 @@
|
|||||||
//! memory usage and higher CPU usage - you will be trying to process more and
|
//! memory usage and higher CPU usage - you will be trying to process more and
|
||||||
//! more points as this is used!
|
//! more points as this is used!
|
||||||
|
|
||||||
use std::{time::Instant, vec::Vec};
|
use std::{collections::BTreeMap, time::Instant, vec::Vec};
|
||||||
|
|
||||||
use fxhash::FxHashMap;
|
use fxhash::FxHashMap;
|
||||||
use itertools::Itertools;
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
@ -48,7 +47,7 @@ pub struct TimedData {
|
|||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct ProcessData {
|
pub struct ProcessData {
|
||||||
/// A PID to process data map.
|
/// A PID to process data map.
|
||||||
pub process_harvest: FxHashMap<Pid, ProcessHarvest>,
|
pub process_harvest: BTreeMap<Pid, ProcessHarvest>,
|
||||||
|
|
||||||
/// A mapping between a process PID to any children process PIDs.
|
/// A mapping between a process PID to any children process PIDs.
|
||||||
pub process_parent_mapping: FxHashMap<Pid, Vec<Pid>>,
|
pub process_parent_mapping: FxHashMap<Pid, Vec<Pid>>,
|
||||||
@ -84,22 +83,14 @@ impl ProcessData {
|
|||||||
// We collect all processes that either:
|
// We collect all processes that either:
|
||||||
// - Do not have a parent PID (that is, they are orphan processes)
|
// - Do not have a parent PID (that is, they are orphan processes)
|
||||||
// - Have a parent PID but we don't have the parent (we promote them as orphans)
|
// - Have a parent PID but we don't have the parent (we promote them as orphans)
|
||||||
// Note this also needs a quick sort + reverse to be in the correct order.
|
self.orphan_pids = self
|
||||||
self.orphan_pids = {
|
|
||||||
let mut res: Vec<Pid> = self
|
|
||||||
.process_harvest
|
.process_harvest
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(pid, process_harvest)| match process_harvest.parent_pid {
|
.filter_map(|(pid, process_harvest)| match process_harvest.parent_pid {
|
||||||
Some(parent_pid) if self.process_harvest.contains_key(&parent_pid) => None,
|
Some(parent_pid) if self.process_harvest.contains_key(&parent_pid) => None,
|
||||||
_ => Some(*pid),
|
_ => Some(*pid),
|
||||||
})
|
})
|
||||||
.sorted()
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
res.reverse();
|
|
||||||
|
|
||||||
res
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,7 +321,7 @@ impl DataCollector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if self.widgets_to_harvest.use_proc {
|
if self.widgets_to_harvest.use_proc {
|
||||||
if let Ok(process_list) = {
|
if let Ok(mut process_list) = {
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
{
|
||||||
processes::get_process_data(
|
processes::get_process_data(
|
||||||
@ -357,6 +357,9 @@ impl DataCollector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} {
|
} {
|
||||||
|
// NB: To avoid duplicate sorts on rerenders/events, we sort the processes by PID here.
|
||||||
|
// We also want to avoid re-sorting *again* since this process list is sorted!
|
||||||
|
process_list.sort_unstable_by_key(|p| p.pid);
|
||||||
self.data.list_of_processes = Some(process_list);
|
self.data.list_of_processes = Some(process_list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ pub async fn get_disk_usage(
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut vec_disks: Vec<DiskHarvest> = get_disk_info().map(|storage_system_information| {
|
let vec_disks: Vec<DiskHarvest> = get_disk_info().map(|storage_system_information| {
|
||||||
storage_system_information
|
storage_system_information
|
||||||
.filesystem
|
.filesystem
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -80,7 +80,6 @@ pub async fn get_disk_usage(
|
|||||||
.collect()
|
.collect()
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
vec_disks.sort_by(|a, b| a.name.cmp(&b.name));
|
|
||||||
Ok(Some(vec_disks))
|
Ok(Some(vec_disks))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +135,5 @@ pub async fn get_disk_usage(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vec_disks.sort_by(|a, b| a.name.cmp(&b.name));
|
|
||||||
|
|
||||||
Ok(Some(vec_disks))
|
Ok(Some(vec_disks))
|
||||||
}
|
}
|
||||||
|
@ -16,8 +16,6 @@ cfg_if::cfg_if! {
|
|||||||
#[cfg(feature = "nvidia")]
|
#[cfg(feature = "nvidia")]
|
||||||
pub mod nvidia;
|
pub mod nvidia;
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
|
||||||
|
|
||||||
use crate::app::Filter;
|
use crate::app::Filter;
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
@ -65,20 +63,3 @@ fn is_temp_filtered(filter: &Option<Filter>, text: &str) -> bool {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn temp_vec_sort(temperature_vec: &mut [TempHarvest]) {
|
|
||||||
// By default, sort temperature, then by alphabetically!
|
|
||||||
// TODO: [TEMPS] Allow users to control this.
|
|
||||||
|
|
||||||
// Note we sort in reverse here; we want greater temps to be higher priority.
|
|
||||||
temperature_vec.sort_by(|a, b| match a.temperature.partial_cmp(&b.temperature) {
|
|
||||||
Some(x) => match x {
|
|
||||||
Ordering::Less => Ordering::Greater,
|
|
||||||
Ordering::Greater => Ordering::Less,
|
|
||||||
Ordering::Equal => Ordering::Equal,
|
|
||||||
},
|
|
||||||
None => Ordering::Equal,
|
|
||||||
});
|
|
||||||
|
|
||||||
temperature_vec.sort_by(|a, b| a.name.partial_cmp(&b.name).unwrap_or(Ordering::Equal));
|
|
||||||
}
|
|
||||||
|
@ -4,7 +4,7 @@ use std::{fs, path::Path};
|
|||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
|
||||||
use super::{is_temp_filtered, temp_vec_sort, TempHarvest, TemperatureType};
|
use super::{is_temp_filtered, TempHarvest, TemperatureType};
|
||||||
use crate::app::{
|
use crate::app::{
|
||||||
data_harvester::temperature::{convert_celsius_to_fahrenheit, convert_celsius_to_kelvin},
|
data_harvester::temperature::{convert_celsius_to_fahrenheit, convert_celsius_to_kelvin},
|
||||||
Filter,
|
Filter,
|
||||||
@ -245,6 +245,5 @@ pub fn get_temperature_data(
|
|||||||
super::nvidia::add_nvidia_data(&mut temperature_vec, temp_type, filter)?;
|
super::nvidia::add_nvidia_data(&mut temperature_vec, temp_type, filter)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
temp_vec_sort(&mut temperature_vec);
|
|
||||||
Ok(Some(temperature_vec))
|
Ok(Some(temperature_vec))
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
convert_celsius_to_fahrenheit, convert_celsius_to_kelvin, is_temp_filtered, temp_vec_sort,
|
convert_celsius_to_fahrenheit, convert_celsius_to_kelvin, is_temp_filtered, TempHarvest,
|
||||||
TempHarvest, TemperatureType,
|
TemperatureType,
|
||||||
};
|
};
|
||||||
use crate::app::Filter;
|
use crate::app::Filter;
|
||||||
|
|
||||||
@ -38,6 +38,5 @@ pub fn get_temperature_data(
|
|||||||
super::nvidia::add_nvidia_data(&mut temperature_vec, temp_type, filter)?;
|
super::nvidia::add_nvidia_data(&mut temperature_vec, temp_type, filter)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
temp_vec_sort(&mut temperature_vec);
|
|
||||||
Ok(Some(temperature_vec))
|
Ok(Some(temperature_vec))
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ use crate::{
|
|||||||
utils::gen_util::{get_decimal_bytes, sort_partial_fn, truncate_text},
|
utils::gen_util::{get_decimal_bytes, sort_partial_fn, truncate_text},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct DiskWidgetData {
|
pub struct DiskWidgetData {
|
||||||
pub name: KString,
|
pub name: KString,
|
||||||
pub mount_point: KString,
|
pub mount_point: KString,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::borrow::Cow;
|
use std::{borrow::Cow, collections::BTreeMap};
|
||||||
|
|
||||||
use fxhash::{FxHashMap, FxHashSet};
|
use fxhash::{FxHashMap, FxHashSet};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
@ -13,7 +13,7 @@ use crate::{
|
|||||||
canvas::canvas_colours::CanvasColours,
|
canvas::canvas_colours::CanvasColours,
|
||||||
components::data_table::{
|
components::data_table::{
|
||||||
Column, ColumnHeader, ColumnWidthBounds, DataTable, DataTableColumn, DataTableProps,
|
Column, ColumnHeader, ColumnWidthBounds, DataTable, DataTableColumn, DataTableProps,
|
||||||
DataTableStyling, SortColumn, SortDataTable, SortDataTableProps, SortOrder,
|
DataTableStyling, SortColumn, SortDataTable, SortDataTableProps, SortOrder, SortsRow,
|
||||||
},
|
},
|
||||||
Pid,
|
Pid,
|
||||||
};
|
};
|
||||||
@ -384,7 +384,9 @@ impl ProcWidget {
|
|||||||
})
|
})
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
|
|
||||||
self.try_sort(&mut stack);
|
stack.sort_unstable_by_key(|p| p.pid);
|
||||||
|
self.try_sort_skip_pid_asc(&mut stack);
|
||||||
|
stack.reverse();
|
||||||
|
|
||||||
let mut length_stack = vec![stack.len()];
|
let mut length_stack = vec![stack.len()];
|
||||||
|
|
||||||
@ -447,6 +449,7 @@ impl ProcWidget {
|
|||||||
data.push(process.prefix(Some(prefix)).disabled(disabled));
|
data.push(process.prefix(Some(prefix)).disabled(disabled));
|
||||||
|
|
||||||
if let Some(children_pids) = filtered_tree.get(&pid) {
|
if let Some(children_pids) = filtered_tree.get(&pid) {
|
||||||
|
// TODO: Can probably use static strings for prefixes rather than allocating.
|
||||||
if prefixes.is_empty() {
|
if prefixes.is_empty() {
|
||||||
prefixes.push(String::default());
|
prefixes.push(String::default());
|
||||||
} else {
|
} else {
|
||||||
@ -485,7 +488,7 @@ impl ProcWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_normal_data(
|
fn get_normal_data(
|
||||||
&mut self, process_harvest: &FxHashMap<Pid, ProcessHarvest>,
|
&mut self, process_harvest: &BTreeMap<Pid, ProcessHarvest>,
|
||||||
) -> Vec<ProcWidgetData> {
|
) -> Vec<ProcWidgetData> {
|
||||||
let search_query = self.get_query();
|
let search_query = self.get_query();
|
||||||
let is_using_command = self.is_using_command();
|
let is_using_command = self.is_using_command();
|
||||||
@ -545,17 +548,10 @@ impl ProcWidget {
|
|||||||
};
|
};
|
||||||
|
|
||||||
self.id_pid_map = id_pid_map;
|
self.id_pid_map = id_pid_map;
|
||||||
self.try_sort(&mut filtered_data);
|
self.try_sort_skip_pid_asc(&mut filtered_data);
|
||||||
filtered_data
|
filtered_data
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn try_sort(&self, filtered_data: &mut [ProcWidgetData]) {
|
|
||||||
if let Some(column) = self.table.columns.get(self.table.sort_index()) {
|
|
||||||
column.sort_by(filtered_data, self.table.order());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn try_rev_sort(&self, filtered_data: &mut [ProcWidgetData]) {
|
fn try_rev_sort(&self, filtered_data: &mut [ProcWidgetData]) {
|
||||||
if let Some(column) = self.table.columns.get(self.table.sort_index()) {
|
if let Some(column) = self.table.columns.get(self.table.sort_index()) {
|
||||||
@ -569,6 +565,16 @@ impl ProcWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn try_sort_skip_pid_asc(&self, filtered_data: &mut [ProcWidgetData]) {
|
||||||
|
if let Some(column) = self.table.columns.get(self.table.sort_index()) {
|
||||||
|
let column = column.inner();
|
||||||
|
let descending = matches!(self.table.order(), SortOrder::Descending);
|
||||||
|
|
||||||
|
sort_skip_pid_asc(column, filtered_data, descending);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn get_mut_proc_col(&mut self, index: usize) -> Option<&mut ProcColumn> {
|
fn get_mut_proc_col(&mut self, index: usize) -> Option<&mut ProcColumn> {
|
||||||
self.table.columns.get_mut(index).map(|col| col.inner_mut())
|
self.table.columns.get_mut(index).map(|col| col.inner_mut())
|
||||||
@ -855,3 +861,113 @@ impl ProcWidget {
|
|||||||
self.force_rerender_and_update();
|
self.force_rerender_and_update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sort_skip_pid_asc(column: &ProcColumn, data: &mut [ProcWidgetData], descending: bool) {
|
||||||
|
match column {
|
||||||
|
ProcColumn::Pid if !descending => {}
|
||||||
|
_ => {
|
||||||
|
column.sort_data(data, descending);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use crate::app::widgets::MemUsage;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sorting_trees() {
|
||||||
|
// FIXME: Add a test for this...
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_proc_sort() {
|
||||||
|
let a = ProcWidgetData {
|
||||||
|
pid: 1,
|
||||||
|
ppid: None,
|
||||||
|
id: "A".into(),
|
||||||
|
cpu_usage_percent: 0.0,
|
||||||
|
mem_usage: MemUsage::Percent(1.1),
|
||||||
|
rps: 0,
|
||||||
|
wps: 0,
|
||||||
|
total_read: 0,
|
||||||
|
total_write: 0,
|
||||||
|
process_state: "N/A".to_string(),
|
||||||
|
process_char: '?',
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
user: "root".to_string(),
|
||||||
|
num_similar: 0,
|
||||||
|
disabled: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let b = ProcWidgetData {
|
||||||
|
pid: 2,
|
||||||
|
id: "B".into(),
|
||||||
|
cpu_usage_percent: 1.1,
|
||||||
|
mem_usage: MemUsage::Percent(2.2),
|
||||||
|
..(a.clone())
|
||||||
|
};
|
||||||
|
|
||||||
|
let c = ProcWidgetData {
|
||||||
|
pid: 3,
|
||||||
|
id: "C".into(),
|
||||||
|
cpu_usage_percent: 2.2,
|
||||||
|
mem_usage: MemUsage::Percent(0.0),
|
||||||
|
..(a.clone())
|
||||||
|
};
|
||||||
|
|
||||||
|
let d = ProcWidgetData {
|
||||||
|
pid: 4,
|
||||||
|
id: "D".into(),
|
||||||
|
cpu_usage_percent: 0.0,
|
||||||
|
mem_usage: MemUsage::Percent(0.0),
|
||||||
|
..(a.clone())
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut data = vec![d.clone(), b.clone(), c.clone(), a.clone()];
|
||||||
|
|
||||||
|
// Assume we had sorted over by pid.
|
||||||
|
data.sort_by_key(|p| p.pid);
|
||||||
|
sort_skip_pid_asc(&ProcColumn::CpuPercent, &mut data, true);
|
||||||
|
assert_eq!(
|
||||||
|
vec![&c, &b, &a, &d]
|
||||||
|
.iter()
|
||||||
|
.map(|d| (d.pid))
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
data.iter().map(|d| (d.pid)).collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Note that the PID ordering for ties is still ascending.
|
||||||
|
data.sort_by_key(|p| p.pid);
|
||||||
|
sort_skip_pid_asc(&ProcColumn::CpuPercent, &mut data, false);
|
||||||
|
assert_eq!(
|
||||||
|
vec![&a, &d, &b, &c]
|
||||||
|
.iter()
|
||||||
|
.map(|d| (d.pid))
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
data.iter().map(|d| (d.pid)).collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
|
||||||
|
data.sort_by_key(|p| p.pid);
|
||||||
|
sort_skip_pid_asc(&ProcColumn::MemoryPercent, &mut data, true);
|
||||||
|
assert_eq!(
|
||||||
|
vec![&b, &a, &c, &d]
|
||||||
|
.iter()
|
||||||
|
.map(|d| (d.pid))
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
data.iter().map(|d| (d.pid)).collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Note that the PID ordering for ties is still ascending.
|
||||||
|
data.sort_by_key(|p| p.pid);
|
||||||
|
sort_skip_pid_asc(&ProcColumn::MemoryPercent, &mut data, false);
|
||||||
|
assert_eq!(
|
||||||
|
vec![&c, &d, &a, &b]
|
||||||
|
.iter()
|
||||||
|
.map(|d| (d.pid))
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
data.iter().map(|d| (d.pid)).collect::<Vec<_>>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -16,18 +16,27 @@ use crate::{
|
|||||||
Pid,
|
Pid,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
enum IdType {
|
enum IdType {
|
||||||
Name(String),
|
Name(String),
|
||||||
Command(String),
|
Command(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Id {
|
pub struct Id {
|
||||||
id_type: IdType,
|
id_type: IdType,
|
||||||
prefix: Option<String>,
|
prefix: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&'static str> for Id {
|
||||||
|
fn from(s: &'static str) -> Self {
|
||||||
|
Id {
|
||||||
|
id_type: IdType::Name(s.to_string()),
|
||||||
|
prefix: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Id {
|
impl Id {
|
||||||
/// Returns the ID as a lowercase [`String`], with no prefix. This is primarily useful for
|
/// Returns the ID as a lowercase [`String`], with no prefix. This is primarily useful for
|
||||||
/// cases like sorting where we treat everything as the same case (e.g. `Discord` comes before
|
/// cases like sorting where we treat everything as the same case (e.g. `Discord` comes before
|
||||||
@ -73,7 +82,7 @@ impl Display for Id {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Can reduce this to 32 bytes.
|
// TODO: Can reduce this to 32 bytes.
|
||||||
#[derive(PartialEq, Clone)]
|
#[derive(PartialEq, Clone, Debug)]
|
||||||
pub enum MemUsage {
|
pub enum MemUsage {
|
||||||
Percent(f64),
|
Percent(f64),
|
||||||
Bytes(u64),
|
Bytes(u64),
|
||||||
@ -98,7 +107,7 @@ impl Display for MemUsage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ProcWidgetData {
|
pub struct ProcWidgetData {
|
||||||
pub pid: Pid,
|
pub pid: Pid,
|
||||||
pub ppid: Option<Pid>,
|
pub ppid: Option<Pid>,
|
||||||
|
@ -14,7 +14,7 @@ use crate::{
|
|||||||
utils::gen_util::{sort_partial_fn, truncate_text},
|
utils::gen_util::{sort_partial_fn, truncate_text},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct TempWidgetData {
|
pub struct TempWidgetData {
|
||||||
pub sensor: KString,
|
pub sensor: KString,
|
||||||
pub temperature_value: u64,
|
pub temperature_value: u64,
|
||||||
@ -78,7 +78,7 @@ impl SortsRow for TempWidgetColumn {
|
|||||||
fn sort_data(&self, data: &mut [Self::DataType], descending: bool) {
|
fn sort_data(&self, data: &mut [Self::DataType], descending: bool) {
|
||||||
match self {
|
match self {
|
||||||
TempWidgetColumn::Sensor => {
|
TempWidgetColumn::Sensor => {
|
||||||
data.sort_by(|a, b| sort_partial_fn(descending)(&a.sensor, &b.sensor));
|
data.sort_by(move |a, b| sort_partial_fn(descending)(&a.sensor, &b.sensor));
|
||||||
}
|
}
|
||||||
TempWidgetColumn::Temp => {
|
TempWidgetColumn::Temp => {
|
||||||
data.sort_by(|a, b| {
|
data.sort_by(|a, b| {
|
||||||
|
@ -111,7 +111,7 @@ pub fn truncate_text<'a, U: Into<usize>>(content: &str, width: U) -> Text<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn sort_partial_fn<T: std::cmp::PartialOrd>(is_descending: bool) -> fn(T, T) -> Ordering {
|
pub const fn sort_partial_fn<T: std::cmp::PartialOrd>(is_descending: bool) -> fn(T, T) -> Ordering {
|
||||||
if is_descending {
|
if is_descending {
|
||||||
partial_ordering_desc
|
partial_ordering_desc
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user