nm_wifi/ui/
header_footer.rs1use ratatui::{
2 Frame,
3 layout::{Alignment, Constraint, Direction, Layout, Rect},
4 style::{Modifier, Style},
5 text::{Line, Span},
6 widgets::{Block, Borders, Paragraph},
7};
8
9use crate::{
10 app_state::{App, AppState},
11 theme::CatppuccinColors,
12};
13
14pub fn keybindings_hint(state: &AppState) -> &'static str {
15 match state {
16 AppState::NetworkList => {
17 "↑↓/jk Move Enter Connect d Disconnect r Rescan i Info h Help q Quit"
18 }
19 AppState::Help => "h/q/Esc Back",
20 AppState::NetworkDetails => "q/i/Esc Back",
21 AppState::PasswordInput => "Enter Connect Tab Show/Hide Esc Cancel",
22 AppState::Connecting | AppState::Disconnecting => "Esc Quit",
23 AppState::Scanning => "Scanning Esc Quit",
24 AppState::ConnectionResult => "Enter Return q/Esc Quit",
25 }
26}
27
28pub fn render_header(f: &mut Frame, app: &App, area: Rect) {
29 let header_chunks = Layout::default()
30 .direction(Direction::Horizontal)
31 .constraints([
32 Constraint::Length(30),
33 Constraint::Min(0),
34 Constraint::Length(25),
35 ])
36 .split(area);
37
38 let title = Paragraph::new(Line::from(vec![
39 Span::styled(
40 "nm-wifi",
41 Style::default()
42 .fg(CatppuccinColors::MAUVE)
43 .add_modifier(Modifier::BOLD),
44 ),
45 Span::styled(
46 concat!(" v", env!("CARGO_PKG_VERSION")),
47 Style::default().fg(CatppuccinColors::SUBTEXT1),
48 ),
49 ]))
50 .block(Block::default().borders(Borders::ALL))
51 .style(Style::default().bg(CatppuccinColors::BASE));
52
53 let scan_info = if let Some(scan_time) = app.last_scan_time {
54 let elapsed = scan_time.elapsed().as_secs();
55 format!(
56 "Networks: {} | Last scan: {}s ago",
57 app.network_count, elapsed
58 )
59 } else {
60 format!("Networks: {}", app.network_count)
61 };
62
63 let info = Paragraph::new(scan_info)
64 .block(Block::default().borders(Borders::ALL))
65 .style(
66 Style::default()
67 .fg(CatppuccinColors::TEXT)
68 .bg(CatppuccinColors::BASE),
69 )
70 .alignment(Alignment::Center);
71
72 let adapter_text = app.adapter_name.as_deref().unwrap_or("WiFi Adapter");
73 let adapter = Paragraph::new(adapter_text)
74 .block(Block::default().borders(Borders::ALL))
75 .style(
76 Style::default()
77 .fg(CatppuccinColors::BLUE)
78 .bg(CatppuccinColors::BASE),
79 )
80 .alignment(Alignment::Center);
81
82 f.render_widget(title, header_chunks[0]);
83 f.render_widget(info, header_chunks[1]);
84 f.render_widget(adapter, header_chunks[2]);
85}
86
87pub fn render_status_bar(f: &mut Frame, app: &App, area: Rect) {
88 let status_chunks = Layout::default()
89 .direction(Direction::Horizontal)
90 .constraints([Constraint::Min(0), Constraint::Length(40)])
91 .split(area);
92
93 let status = Paragraph::new(app.status_message.as_str())
94 .block(Block::default().borders(Borders::ALL))
95 .style(
96 Style::default()
97 .fg(CatppuccinColors::SUBTEXT1)
98 .bg(CatppuccinColors::BASE),
99 )
100 .alignment(Alignment::Left);
101
102 let hints = Paragraph::new(keybindings_hint(&app.state))
103 .block(Block::default().borders(Borders::ALL))
104 .style(
105 Style::default()
106 .fg(CatppuccinColors::OVERLAY1)
107 .bg(CatppuccinColors::BASE),
108 )
109 .alignment(Alignment::Center);
110
111 f.render_widget(status, status_chunks[0]);
112 f.render_widget(hints, status_chunks[1]);
113}