liftof_tui/tabs/
tab_home.rs1use std::collections::{
4 HashMap,
5 VecDeque,
6};
7
8use std::time::{
9 Instant,
10 };
12
13use std::sync::{
14 Arc,
15 Mutex,
16};
17use ratatui::prelude::*;
18
19use ratatui::Frame;
21use ratatui::layout::Rect;
22use ratatui::widgets::{
23 Block,
24 BorderType,
25 Borders,
26 Paragraph,
27 Table,
28 Row,
29};
30
31use liftof_lib::LIFTOF_LOGO_SHOW;
32use crate::colors::ColorTheme;
33
34
35#[derive(Debug, Clone)]
36pub struct HomeTab<'a> {
37 pub theme : ColorTheme,
38 pub streamer : Arc<Mutex<VecDeque<String>>>,
39 pub pack_stat : Arc<Mutex<HashMap<&'a str, usize>>>,
40 pub stream : String,
41 pub stream_max : usize,
42 start_time : Instant,
43}
44
45impl HomeTab<'_> {
46 pub fn new(theme : ColorTheme,
47 streamer : Arc<Mutex<VecDeque<String>>>,
48 pack_stat : Arc<Mutex<HashMap<&str,usize>>>) -> HomeTab {
49 HomeTab {
50 theme,
51 streamer,
52 pack_stat,
53 stream : String::from(""),
54 stream_max : 30,
55 start_time : Instant::now(),
56 }
57 }
58
59 pub fn render(&mut self, main_window : &Rect, frame : &mut Frame) {
60 let main_chunks = Layout::default()
61 .direction(Direction::Vertical)
62 .constraints(
63 [Constraint::Percentage(70),
64 Constraint::Percentage(30)].as_ref(),
65 )
66 .split(*main_window);
67
68 let upper_chunks = Layout::default()
69 .direction(Direction::Horizontal)
70 .constraints(
71 [Constraint::Percentage(70),
72 Constraint::Percentage(30)].as_ref(),
73 )
74 .split(main_chunks[0]);
75
76 let mut rows = Vec::<Row>::new();
77 let mut sum_pack = 0;
78 let passed_time = self.start_time.elapsed().as_secs_f64();
79 match self.pack_stat.lock() {
80 Err(_err) => (),
81 Ok(mut _stat) => {
82 for k in _stat.keys() {
83 if _stat[k] != 0 {
85 sum_pack += _stat[k];
86 if k.contains("Heart"){
87 rows.push(Row::new(vec![format!(" \u{1f493} {:.1}", _stat[k]),
88 format!("{:.1}", (_stat[k] as f64)/passed_time,),
89 format!("[{}]", k)]));
90 } else {
91 rows.push(Row::new(vec![format!(" \u{279f} {:.1}", _stat[k]),
92 format!("{:.1}", (_stat[k] as f64)/passed_time,),
93 format!("[{}]", k)]));
94 }
95 }
96 }
97 }
98 }
99 rows.push(Row::new(vec![" \u{FE4C}\u{FE4C}\u{FE4C}","\u{FE4C}\u{FE4C}","\u{FE4C}\u{FE4C}\u{FE4C}\u{FE4C}\u{FE4C}\u{FE4C}"]));
100 rows.push(Row::new(vec![format!(" \u{279f}{}", sum_pack),
101 format!("{:.1}/s", (sum_pack as f64)/passed_time),
102 format!("[TOTAL]")]));
103
104 let widths = [Constraint::Percentage(30),
105 Constraint::Percentage(20),
106 Constraint::Percentage(50)];
107 let table = Table::new(rows, widths)
108 .column_spacing(1)
109 .header(
110 Row::new(vec![" N", "\u{1f4e6}/s", "Type"])
111 .bottom_margin(1)
112 .top_margin(1)
113 .style(Style::new().add_modifier(Modifier::UNDERLINED))
114 )
115 .block(Block::new()
116 .title("Packet summary \u{1f4e6}")
117 .borders(Borders::ALL)
118 .border_type(BorderType::Rounded)
119 )
120 .style(self.theme.style());
121
122 let main_view = Paragraph::new(LIFTOF_LOGO_SHOW)
123 .style(self.theme.style())
124 .alignment(Alignment::Center)
125 .block(
126 Block::default()
127 .borders(Borders::NONE)
128 );
129
130 match self.streamer.lock() {
131 Err(_err) => (),
132 Ok(mut _vecdeque) => {
133 self.stream = _vecdeque
134 .iter()
135 .cloned() .collect::<Vec<String>>()
137 .join("\n");
138 while _vecdeque.len() > self.stream_max {
140 _vecdeque.pop_front();
141 }
142 },
143 }
144 let side_view = Paragraph::new(self.stream.clone())
146 .style(self.theme.style())
147 .alignment(Alignment::Left)
148 .block(
149 Block::default()
150 .borders(Borders::ALL)
151 .border_type(BorderType::Rounded)
152 .title("Stream")
153 );
154 frame.render_widget(main_view, upper_chunks[0]);
155 frame.render_widget(side_view, main_chunks[1]);
156 frame.render_widget(table, upper_chunks[1])
157 }
158}
159