From 5781a93c1fddf5aa491a685d18b69e1aa50171d2 Mon Sep 17 00:00:00 2001 From: electria Date: Sat, 15 Aug 2026 18:17:49 -0700 Subject: [PATCH 1/5] fix: properly handle cpu graph widget offset --- src/app/cpu_graph.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/cpu_graph.rs b/src/app/cpu_graph.rs index 38c76ba..6b901ca 100644 --- a/src/app/cpu_graph.rs +++ b/src/app/cpu_graph.rs @@ -31,7 +31,7 @@ impl canvas::Program for CpuGraph { bounds: iced::Rectangle, _cursor: mouse::Cursor, ) -> Vec> { - let geometry = self.cache.draw_with_bounds(renderer, bounds, |frame| { + let geometry = self.cache.draw(renderer, bounds.size(), |frame| { frame.stroke( &canvas::Path::new(|builder| { for (index, value) in self.timeline.iter().enumerate() { From 20f68eaa600917db1ab45850037434874f30c8d4 Mon Sep 17 00:00:00 2001 From: electria Date: Sat, 15 Aug 2026 18:18:43 -0700 Subject: [PATCH 2/5] refactor: use new fn over raw struct initialization --- src/app/cpu_graph.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/cpu_graph.rs b/src/app/cpu_graph.rs index 6b901ca..42a4a22 100644 --- a/src/app/cpu_graph.rs +++ b/src/app/cpu_graph.rs @@ -37,7 +37,7 @@ impl canvas::Program for CpuGraph { for (index, value) in self.timeline.iter().enumerate() { let x = index as f32 / self.timeline.len() as f32 * bounds.width; let y = bounds.height - value / 100. * bounds.height; - builder.line_to(Point { x, y }) + builder.line_to(Point::new(x, y)) } }), canvas::Stroke::default().with_color(CYAN), From 98b7a8d201942f904f1aa9af40669445cab13157 Mon Sep 17 00:00:00 2001 From: electria Date: Sat, 15 Aug 2026 18:16:16 -0700 Subject: [PATCH 3/5] feat: multiple graphs --- src/app/cpu_graph.rs | 19 ++++++++++++- src/app/mod.rs | 60 ++++++++++++++++++++++++++++++----------- src/main.rs | 1 + src/sysinfo_snapshot.rs | 18 +++++++++++++ 4 files changed, 82 insertions(+), 16 deletions(-) create mode 100644 src/sysinfo_snapshot.rs diff --git a/src/app/cpu_graph.rs b/src/app/cpu_graph.rs index 42a4a22..9fe439c 100644 --- a/src/app/cpu_graph.rs +++ b/src/app/cpu_graph.rs @@ -8,11 +8,23 @@ const CYAN: Color = Color::from_rgb8(0, 255, 255); #[derive(Default)] pub struct CpuGraph { - pub cache: canvas::Cache, timeline: VecDeque, + + pub cache: canvas::Cache, } impl CpuGraph { + pub fn new(usage: f32) -> Self { + Self { + timeline: { + let mut vec = VecDeque::new(); + vec.push_front(usage); + vec + }, + ..Default::default() + } + } + pub fn update(&mut self, usage: f32) { self.timeline.push_back(usage); if self.timeline.len() > 1000 { @@ -32,6 +44,11 @@ impl canvas::Program for CpuGraph { _cursor: mouse::Cursor, ) -> Vec> { let geometry = self.cache.draw(renderer, bounds.size(), |frame| { + frame.stroke_rectangle( + Point::new(0., 0.), + bounds.size(), + canvas::Stroke::default().with_color(Color::from_rgb8(255, 0, 0)), + ); frame.stroke( &canvas::Path::new(|builder| { for (index, value) in self.timeline.iter().enumerate() { diff --git a/src/app/mod.rs b/src/app/mod.rs index 1e0262a..d989b14 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -4,24 +4,24 @@ use iced::{ }; use smol::Timer; -use crate::app::cpu_graph::CpuGraph; +use crate::{app::cpu_graph::CpuGraph, sysinfo_snapshot::SysinfoSnapshot}; mod cpu_graph; enum Message { - Cpu(f32), + Info(SysinfoSnapshot), } +#[derive(Default)] struct State { - cpu_graph: CpuGraph, + total_cpu_graph: CpuGraph, + cpu_graphs: Vec<(String, CpuGraph)>, } impl State { fn new() -> (Self, Task) { ( - Self { - cpu_graph: Default::default(), - }, + Self::default(), Task::run( channel(1, async |mut sender| { let mut sys = sysinfo::System::new(); @@ -34,28 +34,58 @@ impl State { loop { sys.refresh_specifics(refreshes); Timer::after(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL).await; - sender.send(sys.global_cpu_usage()).await.unwrap(); + sender.send(SysinfoSnapshot::from(&sys)).await.unwrap(); } }), - Message::Cpu, + Message::Info, ), ) } fn update(&mut self, message: Message) -> Task { match message { - Message::Cpu(value) => { - self.cpu_graph.update(value); - self.cpu_graph.cache.clear(); + Message::Info(snapshot) => { + self.total_cpu_graph.update(snapshot.cpu_total); + self.total_cpu_graph.cache.clear(); + + if self.cpu_graphs.len() != snapshot.cpus.len() { + self.cpu_graphs = snapshot + .cpus + .into_iter() + .map(|(name, usage)| (name, CpuGraph::new(usage))) + .collect(); + } else { + for ((_, graph), (_, usage)) in + self.cpu_graphs.iter_mut().zip(snapshot.cpus.into_iter()) + { + graph.update(usage); + graph.cache.clear(); + } + } } } Task::none() } fn view(&self) -> Element<'_, Message, Theme, Renderer> { - widget::Canvas::new(&self.cpu_graph) - .width(Length::Fill) - .height(Length::Fill) - .into() + widget::row([ + widget::Canvas::new(&self.total_cpu_graph) + .width(Length::Fill) + .height(Length::Fill) + .into(), + widget::grid(self.cpu_graphs.iter().map(|(name, graph)| { + widget::row([ + // widget::text(name).into(), + widget::Canvas::new(graph) + .width(Length::Fill) + .height(Length::Fill) + .into(), + ]) + .into() + })) + // .columns(8) + .into(), + ]) + .into() } } diff --git a/src/main.rs b/src/main.rs index 2e13470..fa3e73b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ mod app; +mod sysinfo_snapshot; fn main() -> Result<(), impl std::error::Error> { app::run() diff --git a/src/sysinfo_snapshot.rs b/src/sysinfo_snapshot.rs new file mode 100644 index 0000000..2e6955c --- /dev/null +++ b/src/sysinfo_snapshot.rs @@ -0,0 +1,18 @@ +#[derive(Clone, Debug)] +pub struct SysinfoSnapshot { + pub cpu_total: f32, + pub cpus: Vec<(String, f32)>, +} + +impl From<&sysinfo::System> for SysinfoSnapshot { + fn from(value: &sysinfo::System) -> Self { + Self { + cpu_total: value.global_cpu_usage(), + cpus: value + .cpus() + .iter() + .map(|cpu| (cpu.name().into(), cpu.cpu_usage())) + .collect(), + } + } +} From 7264d127d939abab0af2c0622b046b0313654e78 Mon Sep 17 00:00:00 2001 From: electria Date: Sat, 15 Aug 2026 18:36:29 -0700 Subject: [PATCH 4/5] feat: improve style --- src/app/mod.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/app/mod.rs b/src/app/mod.rs index d989b14..7f073f5 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -72,17 +72,13 @@ impl State { .width(Length::Fill) .height(Length::Fill) .into(), - widget::grid(self.cpu_graphs.iter().map(|(name, graph)| { - widget::row([ - // widget::text(name).into(), - widget::Canvas::new(graph) - .width(Length::Fill) - .height(Length::Fill) - .into(), - ]) - .into() - })) - // .columns(8) + widget::grid( + self.cpu_graphs + .iter() + .map(|(_name, graph)| widget::Canvas::new(graph).into()), + ) + .columns(2) + .height(Length::Fill) .into(), ]) .into() From 93de64f4b967b6b040987e3da2f484b99405ec26 Mon Sep 17 00:00:00 2001 From: electria Date: Sat, 15 Aug 2026 18:42:53 -0700 Subject: [PATCH 5/5] refactor: remove unused name attribute --- src/app/mod.rs | 9 ++++----- src/sysinfo_snapshot.rs | 8 ++------ 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/app/mod.rs b/src/app/mod.rs index 7f073f5..acc8959 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -15,7 +15,7 @@ enum Message { #[derive(Default)] struct State { total_cpu_graph: CpuGraph, - cpu_graphs: Vec<(String, CpuGraph)>, + cpu_graphs: Vec, } impl State { @@ -51,11 +51,10 @@ impl State { self.cpu_graphs = snapshot .cpus .into_iter() - .map(|(name, usage)| (name, CpuGraph::new(usage))) + .map(|usage| CpuGraph::new(usage)) .collect(); } else { - for ((_, graph), (_, usage)) in - self.cpu_graphs.iter_mut().zip(snapshot.cpus.into_iter()) + for (graph, usage) in self.cpu_graphs.iter_mut().zip(snapshot.cpus.into_iter()) { graph.update(usage); graph.cache.clear(); @@ -75,7 +74,7 @@ impl State { widget::grid( self.cpu_graphs .iter() - .map(|(_name, graph)| widget::Canvas::new(graph).into()), + .map(|graph| widget::Canvas::new(graph).into()), ) .columns(2) .height(Length::Fill) diff --git a/src/sysinfo_snapshot.rs b/src/sysinfo_snapshot.rs index 2e6955c..cb55f06 100644 --- a/src/sysinfo_snapshot.rs +++ b/src/sysinfo_snapshot.rs @@ -1,18 +1,14 @@ #[derive(Clone, Debug)] pub struct SysinfoSnapshot { pub cpu_total: f32, - pub cpus: Vec<(String, f32)>, + pub cpus: Vec, } impl From<&sysinfo::System> for SysinfoSnapshot { fn from(value: &sysinfo::System) -> Self { Self { cpu_total: value.global_cpu_usage(), - cpus: value - .cpus() - .iter() - .map(|cpu| (cpu.name().into(), cpu.cpu_usage())) - .collect(), + cpus: value.cpus().iter().map(|cpu| cpu.cpu_usage()).collect(), } } }