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