1use std::borrow::Cow;
2
3use unicode_width::UnicodeWidthStr;
4
5use crate::mprocs::{
6 config::Config,
7 state::{Scope, State},
8};
9use crate::term::{
10 Color, Grid,
11 attrs::Attrs,
12 grid::{BorderType, Rect},
13};
14
15pub fn render_procs(
16 area: Rect,
17 grid: &mut Grid,
18 state: &mut State,
19 config: &Config,
20) {
21 state.procs_list.fit(area.inner(1), state.procs.len());
22
23 if area.width <= 2 {
24 return;
25 }
26
27 let active = state.scope == Scope::Procs;
28
29 grid.draw_block(
30 area.into(),
31 &if active {
32 BorderType::Thick
33 } else {
34 BorderType::Plain
35 }
36 .chars(),
37 Attrs::default(),
38 );
39 let title_area = Rect {
40 x: area.x + 1,
41 y: area.y,
42 width: area.width - 2,
43 height: 1,
44 };
45 let r = grid.draw_text(
46 title_area,
47 config.proc_list_title.as_str(),
48 if active {
49 Attrs::default().set_bold(true)
50 } else {
51 Attrs::default()
52 },
53 );
54 if state.quitting {
55 let area = title_area.inner((0, 0, 0, r.width + 1));
56 grid.draw_text(
57 area,
58 "QUITTING",
59 Attrs::default()
60 .fg(Color::BLACK)
61 .bg(Color::RED)
62 .set_bold(true),
63 );
64 }
65
66 let range = state.procs_list.visible_range();
67 for (row, index) in range.enumerate() {
68 let proc = if let Some(proc) = state.procs.get(index) {
69 proc
70 } else {
71 continue;
72 };
73
74 let selected = index == state.selected();
75 let attrs = if selected {
76 Attrs::default().bg(crate::term::Color::Idx(240))
77 } else {
78 Attrs::default()
79 };
80 let mut row_area = crate::term::grid::Rect {
81 x: area.x + 1,
82 y: area.y + 1 + row as u16,
83 width: area.width.saturating_sub(2),
84 height: 1,
85 };
86
87 let r = grid.draw_text(row_area, if selected { "•" } else { " " }, attrs);
88 row_area.x += r.width;
89 row_area.width = row_area.width.saturating_sub(r.width);
90
91 let r = grid.draw_text(row_area, proc.name(), attrs);
92 row_area.x += r.width;
93 row_area.width = row_area.width.saturating_sub(r.width);
94
95 let (status_text, status_attrs) = if proc.is_up() {
96 (
97 Cow::from(" UP "),
98 attrs.clone().set_bold(true).fg(Color::BRIGHT_GREEN),
99 )
100 } else {
101 match proc.exit_code() {
102 Some(0) => {
103 (Cow::from(" DOWN (0)"), attrs.clone().fg(Color::BRIGHT_BLUE))
104 }
105 Some(exit_code) => (
106 Cow::from(format!(" DOWN ({})", exit_code)),
107 attrs.clone().fg(Color::BRIGHT_RED),
108 ),
109 None => (Cow::from(" DOWN "), attrs.clone().fg(Color::BRIGHT_BLACK)),
110 }
111 };
112 let status_width = status_text.width() as u16;
113 let r = grid.draw_text(
114 Rect {
115 x: row_area.x.max(row_area.x + row_area.width - status_width),
116 width: status_width.min(row_area.width),
117 ..row_area
118 },
119 &status_text,
120 status_attrs,
121 );
122 row_area.width = row_area.width.saturating_sub(r.width);
123
124 grid.fill_area(row_area, ' ', attrs);
125 }
126}
127
128pub fn procs_get_clicked_index(
129 area: Rect,
130 x: u16,
131 y: u16,
132 state: &State,
133) -> Option<usize> {
134 let inner = area.inner(1);
135 if procs_check_hit(area, x, y) {
136 let index = y - inner.y;
137 let scroll = (state.selected() + 1).saturating_sub(inner.height as usize);
138 let index = index as usize + scroll;
139 if index < state.procs.len() {
140 return Some(index);
141 }
142 }
143 None
144}
145
146pub fn procs_check_hit(area: Rect, x: u16, y: u16) -> bool {
147 area.x < x
148 && area.x + area.width > x + 1
149 && area.y < y
150 && area.y + area.height > y + 1
151}