xref: /DragonOS/tools/debugging/logmonitor/src/ui.rs (revision f5b2038871d3441e1c7f32439ff422957e7ab828)
1 use ratatui::{
2     prelude::{Constraint, Layout, Rect},
3     style::{Color, Modifier, Style},
4     symbols,
5     text::{self, Span, Text},
6     widgets::{Block, Borders, List, ListItem, Sparkline, Tabs},
7     Frame,
8 };
9 
10 use crate::app::App;
11 
12 /// Renders the user interface widgets.
13 pub fn render(app: &mut App, frame: &mut Frame) {
14     // This is where you add new widgets.
15     // See the following resources:
16     // - https://docs.rs/ratatui/latest/ratatui/widgets/index.html
17     // - https://github.com/ratatui-org/ratatui/tree/master/examples
18     // frame.render_widget(
19     //     Paragraph::new(format!(
20     //         "This is a tui template.\n\
21     //             Press `Esc`, `Ctrl-C` or `q` to stop running.\n\
22     //             Press left and right to increment and decrement the counter respectively.\n\
23     //             Counter: {}",
24     //         app.counter
25     //     ))
26     //     .block(
27     //         Block::default()
28     //             .title("Template")
29     //             .title_alignment(Alignment::Center)
30     //             .borders(Borders::ALL)
31     //             .border_type(BorderType::Rounded),
32     //     )
33     //     .style(Style::default().fg(Color::Cyan).bg(Color::Black))
34     //     .alignment(Alignment::Center),
35     //     frame.size(),
36     // )
37 
38     let chunks = Layout::default()
39         .constraints([Constraint::Length(3), Constraint::Min(0)])
40         .split(frame.size());
41     let titles = app
42         .tabs
43         .titles
44         .iter()
45         .map(|t| text::Line::from(Span::styled(*t, Style::default().fg(Color::Green))))
46         .collect();
47     let tabs = Tabs::new(titles)
48         .block(Block::default().borders(Borders::ALL).title(app.title))
49         .highlight_style(Style::default().fg(Color::Yellow))
50         .select(app.tabs.index);
51     frame.render_widget(tabs, chunks[0]);
52 
53     match app.tabs.index {
54         0 => draw_first_tab(frame, app, chunks[1]),
55         _ => {}
56     }
57 }
58 
59 fn draw_first_tab(f: &mut Frame, app: &mut App, area: Rect) {
60     let chunks = Layout::default()
61         .constraints([
62             Constraint::Min(1),
63             Constraint::Min(3),
64             Constraint::Length(7),
65         ])
66         .split(area);
67     draw_memory_logging_speed_gauges(f, app, chunks[0]);
68     // draw_charts(f, app, chunks[1]);
69     draw_footer(f, app, chunks[2]);
70 }
71 
72 /// 绘制内存日志产生数量的图表
73 fn draw_memory_logging_speed_gauges(f: &mut Frame, app: &mut App, area: Rect) {
74     let chunks = Layout::default()
75         .constraints([Constraint::Length(3)])
76         .margin(1)
77         .split(area);
78     let block = Block::default().borders(Borders::ALL).title("Speed:");
79     f.render_widget(block, area);
80 
81     let sparkline = Sparkline::default()
82         .block(Block::default().title("Memory Log Speed:"))
83         .style(Style::default().fg(Color::Green))
84         .data(&app.memory_log_sparkline.points)
85         .bar_set(if app.enhanced_graphics {
86             symbols::bar::NINE_LEVELS
87         } else {
88             symbols::bar::THREE_LEVELS
89         });
90     f.render_widget(sparkline, chunks[0]);
91 }
92 
93 fn draw_footer(f: &mut Frame, app: &mut App, area: Rect) {
94     let _block = Block::default().borders(Borders::ALL).title(Span::styled(
95         "Logs",
96         Style::default()
97             .fg(Color::Magenta)
98             .add_modifier(Modifier::BOLD),
99     ));
100 
101     let info_style = Style::default().fg(Color::Blue);
102     let warning_style = Style::default().fg(Color::Yellow);
103     let error_style = Style::default().fg(Color::Magenta);
104     let critical_style = Style::default().fg(Color::Red);
105 
106     let binding = app.logs().clone();
107     let log_list = binding
108         .iter()
109         .map(|log_str| {
110             let _style = match log_str {
111                 log if log.contains("INFO") => info_style,
112                 log if log.contains("WARNING") => warning_style,
113                 log if log.contains("ERROR") => error_style,
114                 log if log.contains("CRITICAL") => critical_style,
115                 _ => Style::default().fg(Color::White),
116             };
117 
118             // println!("log_str: {}", log_str);
119 
120             ListItem::new(Text::from(log_str.clone()))
121         })
122         .collect::<Vec<ListItem>>();
123 
124     let items_num = 5;
125     let list_to_show = log_list.split_at(if log_list.len() > items_num {
126         log_list.len() - items_num
127     } else {
128         0
129     });
130 
131     let logs =
132         List::new(list_to_show.1).block(Block::default().borders(Borders::ALL).title("List"));
133     f.render_stateful_widget(logs, area, &mut app.stateful_logs.state);
134 }
135