Skip to content

Commit afb75fc

Browse files
committed
handle multipe metrics
1 parent 5e2fdb1 commit afb75fc

File tree

1 file changed

+137
-33
lines changed

1 file changed

+137
-33
lines changed

oryx-tui/src/section/metrics.rs

+137-33
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::{
2+
cmp,
23
sync::{atomic::AtomicBool, Arc, Mutex},
34
thread,
45
time::Duration,
@@ -8,11 +9,12 @@ use crossterm::event::{Event, KeyCode, KeyEvent};
89
use tui_input::{backend::crossterm::EventHandler, Input};
910

1011
use ratatui::{
11-
layout::{Alignment, Constraint, Direction, Flex, Layout, Rect},
12+
layout::{Alignment, Constraint, Direction, Flex, Layout, Margin, Rect},
1213
style::{Color, Style, Stylize},
1314
text::Line,
1415
widgets::{
15-
Bar, BarChart, BarGroup, Block, Borders, Cell, Clear, HighlightSpacing, Padding, Row, Table,
16+
Bar, BarChart, BarGroup, Block, BorderType, Borders, Cell, Clear, HighlightSpacing,
17+
Padding, Row, Table,
1618
},
1719
Frame,
1820
};
@@ -26,12 +28,20 @@ use crate::{
2628
},
2729
};
2830

31+
#[derive(Debug, Default)]
32+
struct ListState {
33+
offset: usize,
34+
selected: Option<usize>,
35+
}
36+
2937
#[derive(Debug)]
3038
pub struct Metrics {
3139
user_input: UserInput,
3240
app_packets: Arc<Mutex<Vec<AppPacket>>>,
33-
port_count: Option<Arc<Mutex<PortCountMetric>>>,
41+
metrics: Vec<Arc<Mutex<PortCountMetric>>>,
3442
terminate: Arc<AtomicBool>,
43+
state: ListState,
44+
window_height: usize,
3545
}
3646

3747
#[derive(Debug, Clone, Default)]
@@ -72,29 +82,51 @@ impl Metrics {
7282
Self {
7383
user_input: UserInput::default(),
7484
app_packets: packets,
75-
port_count: None,
85+
metrics: Vec::new(),
7686
terminate: Arc::new(AtomicBool::new(false)),
87+
state: ListState::default(),
88+
window_height: 0,
7789
}
7890
}
7991

80-
pub fn render(&self, frame: &mut Frame, block: Rect) {
81-
let layout = Layout::default()
82-
.direction(Direction::Vertical)
83-
.constraints([Constraint::Length(4), Constraint::Fill(1)])
84-
.flex(ratatui::layout::Flex::SpaceBetween)
85-
.split(block);
92+
pub fn render(&mut self, frame: &mut Frame, block: Rect) {
93+
self.window_height = block.height.saturating_sub(4) as usize / 8;
8694

87-
let block = Layout::default()
88-
.direction(Direction::Horizontal)
89-
.constraints([
90-
Constraint::Fill(1),
91-
Constraint::Percentage(90),
92-
Constraint::Fill(1),
93-
])
94-
.flex(ratatui::layout::Flex::SpaceBetween)
95-
.split(layout[1])[1];
95+
let constraints: Vec<Constraint> = (0..self.window_height)
96+
.map(|_| Constraint::Length(8))
97+
.collect();
9698

97-
if let Some(port_count_metric) = &self.port_count {
99+
let chunks = Layout::default()
100+
.direction(Direction::Vertical)
101+
.constraints(constraints)
102+
.split(block.inner(Margin {
103+
horizontal: 0,
104+
vertical: 2,
105+
}));
106+
107+
let blocks: Vec<_> = chunks
108+
.iter()
109+
.map(|b| {
110+
Layout::default()
111+
.direction(Direction::Horizontal)
112+
.constraints([
113+
Constraint::Fill(1),
114+
Constraint::Percentage(90),
115+
Constraint::Fill(1),
116+
])
117+
.flex(ratatui::layout::Flex::SpaceBetween)
118+
.split(*b)[1]
119+
})
120+
.collect();
121+
122+
let metrics_to_display = if self.metrics.len() <= self.window_height {
123+
self.metrics.clone()
124+
} else {
125+
self.metrics[self.state.offset..self.state.offset + self.window_height].to_vec()
126+
};
127+
128+
// for (index, port_count_metric) in self.metrics[self.state.offset..].iter().enumerate() {
129+
for (index, port_count_metric) in metrics_to_display.iter().enumerate() {
98130
let metric = { port_count_metric.lock().unwrap().clone() };
99131

100132
let chart = BarChart::default()
@@ -121,21 +153,88 @@ impl Metrics {
121153
.block(
122154
Block::new()
123155
.title_alignment(Alignment::Center)
124-
.padding(Padding::vertical(1))
156+
.borders(Borders::LEFT)
157+
.border_style({
158+
if self.state.selected.unwrap() - self.state.offset == index {
159+
Style::new().fg(Color::Magenta)
160+
} else {
161+
Style::new().fg(Color::Gray)
162+
}
163+
})
164+
.border_type({
165+
if self.state.selected.unwrap() - self.state.offset == index {
166+
BorderType::Thick
167+
} else {
168+
BorderType::Plain
169+
}
170+
})
171+
.padding(Padding::uniform(1))
125172
.title_top(format!("Port: {}", metric.port)),
126173
);
127-
frame.render_widget(chart, block);
174+
frame.render_widget(
175+
chart,
176+
blocks[index].inner(Margin {
177+
horizontal: 0,
178+
vertical: 1,
179+
}),
180+
);
128181
}
129182
}
130183

131184
pub fn handle_keys(&mut self, key_event: KeyEvent) {
132-
if let KeyCode::Char('d') = key_event.code {
133-
self.terminate
134-
.store(true, std::sync::atomic::Ordering::Relaxed);
135-
self.port_count = None;
136-
self.user_input.clear();
137-
self.terminate
138-
.store(false, std::sync::atomic::Ordering::Relaxed);
185+
match key_event.code {
186+
KeyCode::Char('d') => {
187+
if let Some(selected_item_index) = &mut self.state.selected {
188+
self.terminate
189+
.store(true, std::sync::atomic::Ordering::Relaxed);
190+
191+
let _ = self.metrics.remove(*selected_item_index);
192+
193+
self.user_input.clear();
194+
195+
self.state.selected = Some(selected_item_index.saturating_sub(1));
196+
197+
self.terminate
198+
.store(false, std::sync::atomic::Ordering::Relaxed);
199+
}
200+
}
201+
202+
KeyCode::Char('k') => {
203+
let i = match self.state.selected {
204+
Some(i) => {
205+
if i > self.state.offset {
206+
i - 1
207+
} else if i == self.state.offset && self.state.offset > 0 {
208+
self.state.offset -= 1;
209+
i - 1
210+
} else {
211+
0
212+
}
213+
}
214+
None => 0,
215+
};
216+
217+
self.state.selected = Some(i);
218+
}
219+
220+
KeyCode::Char('j') => {
221+
let i = match self.state.selected {
222+
Some(i) => {
223+
if i < self.window_height - 1 {
224+
cmp::min(i + 1, self.metrics.len() - 1)
225+
} else if self.metrics.len() - 1 == i {
226+
i
227+
} else {
228+
self.state.offset += 1;
229+
i + 1
230+
}
231+
}
232+
None => 0,
233+
};
234+
235+
self.state.selected = Some(i);
236+
}
237+
_ => {}
139238
}
140239
}
141240

@@ -150,14 +249,14 @@ impl Metrics {
150249

151250
let port: u16 = self.user_input.value();
152251

153-
let port_count = Arc::new(Mutex::new(PortCountMetric {
252+
let port_count_metric = Arc::new(Mutex::new(PortCountMetric {
154253
port,
155254
tcp_count: 0,
156255
udp_count: 0,
157256
}));
158257

159258
thread::spawn({
160-
let port_count = port_count.clone();
259+
let port_count_metric = port_count_metric.clone();
161260
let terminate = self.terminate.clone();
162261
let packets = self.app_packets.clone();
163262
move || {
@@ -170,7 +269,7 @@ impl Metrics {
170269
if app_packets.is_empty() {
171270
continue;
172271
}
173-
let mut metric = port_count.lock().unwrap();
272+
let mut metric = port_count_metric.lock().unwrap();
174273
for app_packet in app_packets[last_index..].iter() {
175274
if terminate.load(std::sync::atomic::Ordering::Relaxed) {
176275
break 'main;
@@ -218,7 +317,12 @@ impl Metrics {
218317
}
219318
});
220319

221-
self.port_count = Some(port_count);
320+
self.metrics.push(port_count_metric);
321+
if self.metrics.len() == 1 {
322+
self.state.selected = Some(0);
323+
}
324+
325+
self.user_input.clear();
222326
}
223327

224328
_ => {

0 commit comments

Comments
 (0)