mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-07-20 20:24:47 +02:00
feature: basic sortable temp (#868)
* feature: basic sortable temp * add shortcuts * fix missing shortcut names in header * update changelog * update docs
This commit is contained in:
parent
e6e1e9d688
commit
4d9f5093b2
@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- [#794](https://github.com/ClementTsang/bottom/pull/794): Add GPU memory support for NVIDIA GPUs.
|
- [#794](https://github.com/ClementTsang/bottom/pull/794): Add GPU memory support for NVIDIA GPUs.
|
||||||
- [#806](https://github.com/ClementTsang/bottom/pull/806): Update sysinfo to support M1 macOS temperature sensors.
|
- [#806](https://github.com/ClementTsang/bottom/pull/806): Update sysinfo to support M1 macOS temperature sensors.
|
||||||
- [#841](https://github.com/ClementTsang/bottom/pull/841): Add page up/page down support for the help screen.
|
- [#841](https://github.com/ClementTsang/bottom/pull/841): Add page up/page down support for the help screen.
|
||||||
|
- [#868](https://github.com/ClementTsang/bottom/pull/868): Make temperature widget sortable.
|
||||||
|
|
||||||
## [0.6.8] - 2022-02-01
|
## [0.6.8] - 2022-02-01
|
||||||
|
|
||||||
|
@ -14,12 +14,14 @@ The temperature widget provides the sensor name as well as its current temperatu
|
|||||||
|
|
||||||
Note that key bindings are generally case-sensitive.
|
Note that key bindings are generally case-sensitive.
|
||||||
|
|
||||||
| Binding | Action |
|
| Binding | Action |
|
||||||
| ------------------ | ------------------------------------ |
|
| ------------------ | --------------------------------------------------------- |
|
||||||
| ++up++ , ++k++ | Move up within a widget |
|
| ++up++ , ++k++ | Move up within a widget |
|
||||||
| ++down++ , ++j++ | Move down within a widget |
|
| ++down++ , ++j++ | Move down within a widget |
|
||||||
| ++g+g++ , ++home++ | Jump to the first entry in the table |
|
| ++g+g++ , ++home++ | Jump to the first entry in the table |
|
||||||
| ++G++ , ++end++ | Jump to the last entry in the table |
|
| ++G++ , ++end++ | Jump to the last entry in the table |
|
||||||
|
| ++t++ | Sort by temperature, press again to reverse sorting order |
|
||||||
|
| ++s++ | Sort by sensor name, press again to reverse sorting order |
|
||||||
|
|
||||||
## Mouse bindings
|
## Mouse bindings
|
||||||
|
|
||||||
|
56
src/app.rs
56
src/app.rs
@ -1206,6 +1206,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Refactor this system...
|
||||||
fn handle_char(&mut self, caught_char: char) {
|
fn handle_char(&mut self, caught_char: char) {
|
||||||
match caught_char {
|
match caught_char {
|
||||||
'/' => {
|
'/' => {
|
||||||
@ -1310,12 +1311,34 @@ impl App {
|
|||||||
'L' | 'D' => self.move_widget_selection(&WidgetDirection::Right),
|
'L' | 'D' => self.move_widget_selection(&WidgetDirection::Right),
|
||||||
'K' | 'W' => self.move_widget_selection(&WidgetDirection::Up),
|
'K' | 'W' => self.move_widget_selection(&WidgetDirection::Up),
|
||||||
'J' | 'S' => self.move_widget_selection(&WidgetDirection::Down),
|
'J' | 'S' => self.move_widget_selection(&WidgetDirection::Down),
|
||||||
't' => self.toggle_tree_mode(),
|
't' => {
|
||||||
|
if let BottomWidgetType::Proc = self.current_widget.widget_type {
|
||||||
|
self.toggle_tree_mode()
|
||||||
|
} else if let Some(temp) = self
|
||||||
|
.temp_state
|
||||||
|
.get_mut_widget_state(self.current_widget.widget_id)
|
||||||
|
{
|
||||||
|
temp.table.set_sort_index(1);
|
||||||
|
temp.force_data_update();
|
||||||
|
self.is_force_redraw = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
'+' => self.on_plus(),
|
'+' => self.on_plus(),
|
||||||
'-' => self.on_minus(),
|
'-' => self.on_minus(),
|
||||||
'=' => self.reset_zoom(),
|
'=' => self.reset_zoom(),
|
||||||
'e' => self.toggle_expand_widget(),
|
'e' => self.toggle_expand_widget(),
|
||||||
's' => self.toggle_sort_menu(),
|
's' => {
|
||||||
|
if let BottomWidgetType::Proc = self.current_widget.widget_type {
|
||||||
|
self.toggle_sort_menu()
|
||||||
|
} else if let Some(temp) = self
|
||||||
|
.temp_state
|
||||||
|
.get_mut_widget_state(self.current_widget.widget_id)
|
||||||
|
{
|
||||||
|
temp.table.set_sort_index(0);
|
||||||
|
temp.force_data_update();
|
||||||
|
self.is_force_redraw = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
'I' => self.invert_sort(),
|
'I' => self.invert_sort(),
|
||||||
'%' => self.toggle_percentages(),
|
'%' => self.toggle_percentages(),
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -2562,19 +2585,28 @@ impl App {
|
|||||||
// We might have clicked on a header! Check if we only exceeded the table + border offset, and
|
// We might have clicked on a header! Check if we only exceeded the table + border offset, and
|
||||||
// it's implied we exceeded the gap offset.
|
// it's implied we exceeded the gap offset.
|
||||||
if clicked_entry == border_offset {
|
if clicked_entry == border_offset {
|
||||||
if let BottomWidgetType::Proc = &self.current_widget.widget_type {
|
match &self.current_widget.widget_type {
|
||||||
if let Some(proc_widget_state) = self
|
BottomWidgetType::Proc => {
|
||||||
.proc_state
|
if let Some(state) = self
|
||||||
.get_mut_widget_state(self.current_widget.widget_id)
|
.proc_state
|
||||||
{
|
.get_mut_widget_state(self.current_widget.widget_id)
|
||||||
if proc_widget_state
|
|
||||||
.table
|
|
||||||
.try_select_location(x, y)
|
|
||||||
.is_some()
|
|
||||||
{
|
{
|
||||||
proc_widget_state.force_data_update();
|
if state.table.try_select_location(x, y).is_some() {
|
||||||
|
state.force_data_update();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BottomWidgetType::Temp => {
|
||||||
|
if let Some(state) = self
|
||||||
|
.temp_state
|
||||||
|
.get_mut_widget_state(self.current_widget.widget_id)
|
||||||
|
{
|
||||||
|
if state.table.try_select_location(x, y).is_some() {
|
||||||
|
state.force_data_update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -262,7 +262,7 @@ impl ProcWidget {
|
|||||||
|
|
||||||
/// This function *only* updates the displayed process data. If there is a need to update the actual *stored* data,
|
/// This function *only* updates the displayed process data. If there is a need to update the actual *stored* data,
|
||||||
/// call it before this function.
|
/// call it before this function.
|
||||||
pub fn update_displayed_process_data(&mut self, data_collection: &DataCollection) {
|
pub fn ingest_data(&mut self, data_collection: &DataCollection) {
|
||||||
let data = match &self.mode {
|
let data = match &self.mode {
|
||||||
ProcWidgetMode::Grouped | ProcWidgetMode::Normal => {
|
ProcWidgetMode::Grouped | ProcWidgetMode::Normal => {
|
||||||
self.get_normal_data(&data_collection.process_data.process_harvest)
|
self.get_normal_data(&data_collection.process_data.process_harvest)
|
||||||
|
@ -8,10 +8,10 @@ use crate::{
|
|||||||
app::{data_harvester::temperature::TemperatureType, AppConfigFields},
|
app::{data_harvester::temperature::TemperatureType, AppConfigFields},
|
||||||
canvas::canvas_colours::CanvasColours,
|
canvas::canvas_colours::CanvasColours,
|
||||||
components::data_table::{
|
components::data_table::{
|
||||||
Column, ColumnHeader, DataTable, DataTableColumn, DataTableProps, DataTableStyling,
|
ColumnHeader, DataTableColumn, DataTableProps, DataTableStyling, DataToCell, SortColumn,
|
||||||
DataToCell,
|
SortDataTable, SortDataTableProps, SortOrder, SortsRow,
|
||||||
},
|
},
|
||||||
utils::gen_util::truncate_text,
|
utils::gen_util::{sort_partial_fn, truncate_text},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -29,8 +29,8 @@ pub enum TempWidgetColumn {
|
|||||||
impl ColumnHeader for TempWidgetColumn {
|
impl ColumnHeader for TempWidgetColumn {
|
||||||
fn text(&self) -> Cow<'static, str> {
|
fn text(&self) -> Cow<'static, str> {
|
||||||
match self {
|
match self {
|
||||||
TempWidgetColumn::Sensor => "Sensor".into(),
|
TempWidgetColumn::Sensor => "Sensor(s)".into(),
|
||||||
TempWidgetColumn::Temp => "Temp".into(),
|
TempWidgetColumn::Temp => "Temp(t)".into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,34 +72,67 @@ impl DataToCell<TempWidgetColumn> for TempWidgetData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SortsRow for TempWidgetColumn {
|
||||||
|
type DataType = TempWidgetData;
|
||||||
|
|
||||||
|
fn sort_data(&self, data: &mut [Self::DataType], descending: bool) {
|
||||||
|
match self {
|
||||||
|
TempWidgetColumn::Sensor => {
|
||||||
|
data.sort_by(|a, b| sort_partial_fn(descending)(&a.sensor, &b.sensor));
|
||||||
|
}
|
||||||
|
TempWidgetColumn::Temp => {
|
||||||
|
data.sort_by(|a, b| {
|
||||||
|
sort_partial_fn(descending)(a.temperature_value, b.temperature_value)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct TempWidgetState {
|
pub struct TempWidgetState {
|
||||||
pub table: DataTable<TempWidgetData, TempWidgetColumn>,
|
pub table: SortDataTable<TempWidgetData, TempWidgetColumn>,
|
||||||
|
pub force_update_data: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TempWidgetState {
|
impl TempWidgetState {
|
||||||
pub fn new(config: &AppConfigFields, colours: &CanvasColours) -> Self {
|
pub fn new(config: &AppConfigFields, colours: &CanvasColours) -> Self {
|
||||||
const COLUMNS: [Column<TempWidgetColumn>; 2] = [
|
let columns = [
|
||||||
Column::soft(TempWidgetColumn::Sensor, Some(0.8)),
|
SortColumn::soft(TempWidgetColumn::Sensor, Some(0.8)),
|
||||||
Column::soft(TempWidgetColumn::Temp, None),
|
SortColumn::soft(TempWidgetColumn::Temp, None).default_descending(),
|
||||||
];
|
];
|
||||||
|
|
||||||
let props = DataTableProps {
|
let props = SortDataTableProps {
|
||||||
title: Some(" Temperatures ".into()),
|
inner: DataTableProps {
|
||||||
table_gap: config.table_gap,
|
title: Some(" Temperatures ".into()),
|
||||||
left_to_right: false,
|
table_gap: config.table_gap,
|
||||||
is_basic: config.use_basic_mode,
|
left_to_right: false,
|
||||||
show_table_scroll_position: config.show_table_scroll_position,
|
is_basic: config.use_basic_mode,
|
||||||
show_current_entry_when_unfocused: false,
|
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);
|
let styling = DataTableStyling::from_colours(colours);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
table: DataTable::new(COLUMNS, props, styling),
|
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: &[TempWidgetData]) {
|
pub fn ingest_data(&mut self, data: &[TempWidgetData]) {
|
||||||
self.table.set_data(data.to_vec());
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -196,7 +196,11 @@ fn main() -> Result<()> {
|
|||||||
app.converted_data.ingest_temp_data(
|
app.converted_data.ingest_temp_data(
|
||||||
&app.data_collection,
|
&app.data_collection,
|
||||||
app.app_config_fields.temperature_type,
|
app.app_config_fields.temperature_type,
|
||||||
)
|
);
|
||||||
|
|
||||||
|
for temp in app.temp_state.widget_states.values_mut() {
|
||||||
|
temp.force_data_update();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Memory
|
// Memory
|
||||||
|
@ -330,7 +330,7 @@ pub fn update_data(app: &mut App) {
|
|||||||
|
|
||||||
for proc in app.proc_state.widget_states.values_mut() {
|
for proc in app.proc_state.widget_states.values_mut() {
|
||||||
if proc.force_update_data {
|
if proc.force_update_data {
|
||||||
proc.update_displayed_process_data(data_source);
|
proc.ingest_data(data_source);
|
||||||
proc.force_update_data = false;
|
proc.force_update_data = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -353,7 +353,10 @@ pub fn update_data(app: &mut App) {
|
|||||||
{
|
{
|
||||||
let data = &app.converted_data.temp_data;
|
let data = &app.converted_data.temp_data;
|
||||||
for temp in app.temp_state.widget_states.values_mut() {
|
for temp in app.temp_state.widget_states.values_mut() {
|
||||||
temp.ingest_data(data);
|
if temp.force_update_data {
|
||||||
|
temp.ingest_data(data);
|
||||||
|
temp.force_update_data = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user