@@ -30,14 +30,14 @@ const ROW_HEIGHT: u32 = 22; |
| 30 | 30 | /// Header row height. |
| 31 | 31 | const HEADER_HEIGHT: u32 = 24; |
| 32 | 32 | |
| 33 | | -/// Process list component. |
| 33 | +/// Process list component - renders process data without owning it. |
| 34 | 34 | pub struct ProcessList { |
| 35 | 35 | bounds: Rect, |
| 36 | | - processes: Vec<ProcessInfo>, |
| 37 | 36 | scroll_offset: usize, |
| 38 | 37 | selected: Option<usize>, |
| 39 | 38 | sort_field: SortField, |
| 40 | 39 | visible_rows: usize, |
| 40 | + process_count: usize, |
| 41 | 41 | } |
| 42 | 42 | |
| 43 | 43 | impl ProcessList { |
@@ -46,11 +46,11 @@ impl ProcessList { |
| 46 | 46 | let visible_rows = ((bounds.height.saturating_sub(HEADER_HEIGHT)) / ROW_HEIGHT) as usize; |
| 47 | 47 | Self { |
| 48 | 48 | bounds, |
| 49 | | - processes: Vec::new(), |
| 50 | 49 | scroll_offset: 0, |
| 51 | 50 | selected: None, |
| 52 | 51 | sort_field: SortField::Cpu, |
| 53 | 52 | visible_rows, |
| 53 | + process_count: 0, |
| 54 | 54 | } |
| 55 | 55 | } |
| 56 | 56 | |
@@ -60,9 +60,9 @@ impl ProcessList { |
| 60 | 60 | self.visible_rows = ((bounds.height.saturating_sub(HEADER_HEIGHT)) / ROW_HEIGHT) as usize; |
| 61 | 61 | } |
| 62 | 62 | |
| 63 | | - /// Set processes to display. |
| 64 | | - pub fn set_processes(&mut self, processes: Vec<ProcessInfo>) { |
| 65 | | - self.processes = processes; |
| 63 | + /// Update process count (for scroll calculations). |
| 64 | + pub fn set_process_count(&mut self, count: usize) { |
| 65 | + self.process_count = count; |
| 66 | 66 | // Clamp scroll offset |
| 67 | 67 | if self.scroll_offset > self.max_scroll() { |
| 68 | 68 | self.scroll_offset = self.max_scroll(); |
@@ -81,7 +81,7 @@ impl ProcessList { |
| 81 | 81 | |
| 82 | 82 | /// Maximum scroll offset. |
| 83 | 83 | fn max_scroll(&self) -> usize { |
| 84 | | - self.processes.len().saturating_sub(self.visible_rows) |
| 84 | + self.process_count.saturating_sub(self.visible_rows) |
| 85 | 85 | } |
| 86 | 86 | |
| 87 | 87 | /// Handle scroll (delta is number of rows to scroll, positive = down). |
@@ -94,7 +94,7 @@ impl ProcessList { |
| 94 | 94 | } |
| 95 | 95 | |
| 96 | 96 | /// Handle click, returns selected process PID if any. |
| 97 | | - pub fn on_click(&mut self, pos: Point) -> Option<i32> { |
| 97 | + pub fn on_click(&mut self, pos: Point, processes: &[ProcessInfo]) -> Option<i32> { |
| 98 | 98 | if !self.bounds.contains_point(pos) { |
| 99 | 99 | return None; |
| 100 | 100 | } |
@@ -110,17 +110,17 @@ impl ProcessList { |
| 110 | 110 | let row = ((local_y - HEADER_HEIGHT as i32) / ROW_HEIGHT as i32) as usize; |
| 111 | 111 | let process_idx = self.scroll_offset + row; |
| 112 | 112 | |
| 113 | | - if process_idx < self.processes.len() { |
| 113 | + if process_idx < processes.len() { |
| 114 | 114 | self.selected = Some(process_idx); |
| 115 | | - return Some(self.processes[process_idx].pid); |
| 115 | + return Some(processes[process_idx].pid); |
| 116 | 116 | } |
| 117 | 117 | |
| 118 | 118 | None |
| 119 | 119 | } |
| 120 | 120 | |
| 121 | 121 | /// Get selected process PID. |
| 122 | | - pub fn selected_pid(&self) -> Option<i32> { |
| 123 | | - self.selected.and_then(|idx| self.processes.get(idx).map(|p| p.pid)) |
| 122 | + pub fn selected_pid(&self, processes: &[ProcessInfo]) -> Option<i32> { |
| 123 | + self.selected.and_then(|idx| processes.get(idx).map(|p| p.pid)) |
| 124 | 124 | } |
| 125 | 125 | |
| 126 | 126 | /// Clear selection. |
@@ -129,7 +129,7 @@ impl ProcessList { |
| 129 | 129 | } |
| 130 | 130 | |
| 131 | 131 | /// Render the process list. |
| 132 | | - pub fn render(&self, renderer: &Renderer, theme: &Theme) -> anyhow::Result<()> { |
| 132 | + pub fn render(&self, renderer: &Renderer, theme: &Theme, processes: &[ProcessInfo]) -> anyhow::Result<()> { |
| 133 | 133 | // Background |
| 134 | 134 | renderer.fill_rect(self.bounds, theme.panel_bg)?; |
| 135 | 135 | |
@@ -152,13 +152,18 @@ impl ProcessList { |
| 152 | 152 | ..text_style.clone() |
| 153 | 153 | }; |
| 154 | 154 | |
| 155 | | - // Column positions (PID can be 7 digits, ~56px at 11px monospace) |
| 155 | + // Column positions - adjusted for better spacing |
| 156 | 156 | let x = self.bounds.x as f64; |
| 157 | 157 | let col_pid = x + 8.0; |
| 158 | 158 | let col_name = x + 80.0; |
| 159 | | - let col_cpu = x + 230.0; |
| 160 | | - let col_mem = x + 300.0; |
| 161 | | - let col_user = x + 390.0; |
| 159 | + let col_cpu = x + 240.0; // CPU%/Read/Sock |
| 160 | + let col_mem = x + 310.0; // Mem%/Write/Listen |
| 161 | + let col_extra = x + 380.0; // Estab (network only) |
| 162 | + let col_user = x + 450.0; // User |
| 163 | + |
| 164 | + // Show different columns based on sort field |
| 165 | + let is_disk_sort = matches!(self.sort_field, SortField::DiskRead | SortField::DiskWrite | SortField::DiskTotal); |
| 166 | + let is_net_sort = matches!(self.sort_field, SortField::NetConnections | SortField::NetTcp | SortField::NetBandwidth); |
| 162 | 167 | |
| 163 | 168 | // Header - position text near top (Pango uses top-left positioning) |
| 164 | 169 | let header_y = self.bounds.y as f64 + 4.0; |
@@ -177,13 +182,6 @@ impl ProcessList { |
| 177 | 182 | ..header_style.clone() |
| 178 | 183 | }; |
| 179 | 184 | |
| 180 | | - // Show different columns based on sort field |
| 181 | | - let is_disk_sort = matches!(self.sort_field, SortField::DiskRead | SortField::DiskWrite | SortField::DiskTotal); |
| 182 | | - let is_net_sort = matches!(self.sort_field, SortField::NetConnections | SortField::NetTcp | SortField::NetBandwidth); |
| 183 | | - |
| 184 | | - // Extra column position for network mode |
| 185 | | - let col_extra = x + 350.0; |
| 186 | | - |
| 187 | 185 | if is_disk_sort { |
| 188 | 186 | renderer.text("Read/s", col_cpu, header_y, &sort_style)?; |
| 189 | 187 | renderer.text("Write/s", col_mem, header_y, &sort_style)?; |
@@ -216,7 +214,7 @@ impl ProcessList { |
| 216 | 214 | |
| 217 | 215 | // Process rows |
| 218 | 216 | let start_y = self.bounds.y + HEADER_HEIGHT as i32; |
| 219 | | - for (i, process) in self.processes.iter() |
| 217 | + for (i, process) in processes.iter() |
| 220 | 218 | .skip(self.scroll_offset) |
| 221 | 219 | .take(self.visible_rows) |
| 222 | 220 | .enumerate() |
@@ -316,12 +314,12 @@ impl ProcessList { |
| 316 | 314 | } |
| 317 | 315 | |
| 318 | 316 | // Scroll indicator if needed |
| 319 | | - if self.processes.len() > self.visible_rows { |
| 317 | + if processes.len() > self.visible_rows { |
| 320 | 318 | let scroll_info = format!( |
| 321 | 319 | "{}-{} of {}", |
| 322 | 320 | self.scroll_offset + 1, |
| 323 | | - (self.scroll_offset + self.visible_rows).min(self.processes.len()), |
| 324 | | - self.processes.len() |
| 321 | + (self.scroll_offset + self.visible_rows).min(processes.len()), |
| 322 | + processes.len() |
| 325 | 323 | ); |
| 326 | 324 | let info_style = TextStyle { |
| 327 | 325 | font_size: 9.0, |