Skip to main content

lib/mprocs/
ui_procs.rs

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}