feat: filter out "non-primary" temperatures for immediate display, move them to a popdown (#959)

* feat: filter out "non-primary" temperatures for immediate display, move them to a popdown

* fix: use different item based on presence of secondary sensors
This commit is contained in:
Ilya Zlobintsev
2026-04-18 18:16:16 +03:00
committed by GitHub
parent e01d77e21e
commit 09f1bc1893
499 changed files with 440 additions and 134 deletions
+27 -14
View File
@@ -88,22 +88,36 @@ pub fn fmt_throttling_text(stats: &DeviceStats) -> String {
}
}
pub fn fmt_temperature_text(stats: &DeviceStats) -> Option<String> {
let mut temperatures: Vec<String> = stats
const PRIMARY_TEMP_THRESHOLD: usize = 3;
pub fn fmt_temperature_text(stats: &DeviceStats) -> (Vec<String>, Vec<String>) {
let (mut primary, mut secondary): (Vec<_>, Vec<_>) = stats
.temps
.iter()
.partition(|(_, entry)| stats.temps.len() <= PRIMARY_TEMP_THRESHOLD || entry.primary);
primary.sort_unstable_by(|lhs, rhs| lhs.0.cmp(rhs.0));
secondary.sort_unstable_by(|lhs, rhs| lhs.0.cmp(rhs.0));
let primary = primary
.into_iter()
.filter_map(|(label, temp)| {
temp.value
.current
.map(|current| format!("{label}: {}°C", Mono::float(current, 0)))
})
.collect();
temperatures.sort_unstable();
if temperatures.is_empty() {
None
} else {
Some(temperatures.join(", "))
}
.collect::<Vec<_>>();
let secondary = secondary
.into_iter()
.filter_map(|(label, temp)| {
temp.value
.current
.map(|current| format!("{label}: {}°C", Mono::float(current, 0)))
})
.collect::<Vec<_>>();
(primary, secondary)
}
pub fn fmt_clockspeed(clock_mhz: Option<u64>, ratio: f64) -> String {
@@ -261,6 +275,7 @@ mod tests {
crit_hyst: None,
current: Some(80.0),
},
primary: true,
display_only: false,
},
),
@@ -272,6 +287,7 @@ mod tests {
crit_hyst: None,
current: Some(55.0),
},
primary: true,
display_only: false,
},
),
@@ -280,11 +296,8 @@ mod tests {
};
assert_eq!(
fmt_temperature_text(&stats),
Some(
"edge: <span font_family='monospace'>55</span>°C, junction: <span font_family='monospace'>80</span>°C"
.to_string()
)
fmt_temperature_text(&stats).0.join(", "),
"edge: <span font_family='monospace'>55</span>°C, junction: <span font_family='monospace'>80</span>°C".to_string()
);
}
@@ -193,6 +193,7 @@ mod benches {
crit_hyst: None,
current: Some(56.0),
},
primary: true,
display_only: false,
},
)]),
@@ -8,10 +8,14 @@ use crate::app::{
info_row_level::InfoRowLevel,
page_section::PageSection,
};
use gtk::prelude::{BoxExt, ButtonExt, Cast, FlowBoxChildExt, OrientableExt, WidgetExt};
use gtk::pango::AttrList;
use gtk::prelude::{
BoxExt, ButtonExt, Cast, FlowBoxChildExt, OrientableExt, PopoverExt as _, WidgetExt,
};
use i18n_embed_fl::fl;
use lact_schema::{DeviceInfo, DeviceStats, PowerStates, PowerStats};
use relm4::{ComponentParts, ComponentSender};
use relm4::{ComponentParts, ComponentSender, RelmWidgetExt as _};
use std::str::FromStr as _;
use std::sync::Arc;
pub struct GpuStatsSection {
@@ -89,11 +93,50 @@ impl relm4::SimpleComponent for GpuStatsSection {
set_visible: model.stats.voltage.gpu.is_some(),
},
append = &InfoRow {
append_child = &InfoRow {
set_name: fl!(I18N, "gpu-temp"),
#[watch]
set_value: formatting::fmt_temperature_text(&model.stats)
.unwrap_or_else(|| "N/A".to_owned()),
set_value: if primary_temperatures.is_empty() {
"N/A".to_owned()
} else {
primary_temperatures.join(", ")
},
} -> basic_temps_item: gtk::FlowBoxChild {
#[watch]
set_visible: secondary_temperatures.is_empty(),
},
append_child = &InfoRow {
set_name: fl!(I18N, "gpu-temp"),
#[watch]
set_value: if primary_temperatures.is_empty() {
"N/A".to_owned()
} else {
primary_temperatures.join(", ")
},
set_icon: "go-down-symbolic".to_string(),
#[name = "secondary_temps_popover"]
set_popover = &gtk::Popover {
gtk::Label {
set_margin_all: 10,
set_selectable: false,
set_use_markup: true,
set_attributes: Some(&AttrList::from_str("0 -1 weight bold").unwrap()),
#[watch]
set_label: &secondary_temperatures.join("\n"),
},
},
connect_clicked[secondary_temps_popover] => move |_| {
secondary_temps_popover.popup();
},
} -> full_temps_item: gtk::FlowBoxChild {
#[watch]
set_visible: !secondary_temperatures.is_empty(),
},
},
},
@@ -249,6 +292,9 @@ impl relm4::SimpleComponent for GpuStatsSection {
) -> ComponentParts<Self> {
let value_size_group = gtk::SizeGroup::new(gtk::SizeGroupMode::Horizontal);
let (primary_temperatures, secondary_temperatures): (Vec<String>, Vec<String>) =
(Vec::new(), Vec::new());
let model = Self {
stats: Arc::new(DeviceStats::default()),
vram_clock_ratio: 1.0,
@@ -333,6 +379,11 @@ impl relm4::SimpleComponent for GpuStatsSection {
}
}
}
fn pre_view(&self) {
let (primary_temperatures, secondary_temperatures) =
formatting::fmt_temperature_text(&model.stats);
}
}
fn format_current_gfxclk(value: Option<u64>) -> String {
+6 -1
View File
@@ -423,7 +423,12 @@ impl relm4::Component for ThermalsPage {
}
PageUpdate::Stats(stats) => {
self.fan_speed = fmt_fan_speed(&stats, true);
self.temperatures = fmt_temperature_text(&stats);
let temps = fmt_temperature_text(&stats).0;
self.temperatures = if temps.is_empty() {
None
} else {
Some(temps.join(", "))
};
self.throttling = fmt_throttling_text(&stats);
if initial {