nmrs_gui/ui/
network_page.rs1use glib::clone;
2use gtk::prelude::*;
3use gtk::{Align, Box, Button, Image, Label, Orientation};
4use nmrs::models::NetworkInfo;
5use nmrs::NetworkManager;
6use std::cell::RefCell;
7use std::rc::Rc;
8
9type OnSuccessCallback = Rc<RefCell<Option<Rc<dyn Fn()>>>>;
10
11pub struct NetworkPage {
12 root: gtk::Box,
13
14 title: gtk::Label,
15 status: gtk::Label,
16 strength: gtk::Label,
17 bars: gtk::Label,
18
19 bssid: gtk::Label,
20 freq: gtk::Label,
21 channel: gtk::Label,
22 mode: gtk::Label,
23 rate: gtk::Label,
24 security: gtk::Label,
25
26 current_ssid: Rc<RefCell<String>>,
27 on_success: OnSuccessCallback,
28}
29
30impl NetworkPage {
31 pub fn new(stack: >k::Stack) -> Self {
32 let root = Box::new(Orientation::Vertical, 12);
33 root.add_css_class("network-page");
34
35 let back = Button::with_label("← Back");
36 back.add_css_class("back-button");
37 back.set_halign(Align::Start);
38 back.set_cursor_from_name(Some("pointer"));
39 back.connect_clicked(clone![
40 #[weak]
41 stack,
42 move |_| {
43 stack.set_visible_child_name("networks");
44 }
45 ]);
46 root.append(&back);
47
48 let header = Box::new(Orientation::Horizontal, 6);
49 let icon = Image::from_icon_name("network-wireless-signal-excellent-symbolic");
50 icon.set_pixel_size(24);
51
52 let title = Label::new(None);
53 title.add_css_class("network-title");
54
55 let spacer = Box::new(Orientation::Horizontal, 0);
56 spacer.set_hexpand(true);
57
58 let forget_btn = Button::with_label("Forget");
59 forget_btn.add_css_class("forget-button");
60 forget_btn.set_halign(Align::End);
61 forget_btn.set_valign(Align::Center);
62 forget_btn.set_cursor_from_name(Some("pointer"));
63
64 let current_ssid = Rc::new(RefCell::new(String::new()));
65 let on_success_callback: OnSuccessCallback = Rc::new(RefCell::new(None));
66
67 {
68 let stack_clone = stack.clone();
69 let current_ssid_clone = current_ssid.clone();
70 let on_success_clone = on_success_callback.clone();
71
72 forget_btn.connect_clicked(move |_| {
73 let stack = stack_clone.clone();
74 let ssid = current_ssid_clone.borrow().clone();
75 let on_success = on_success_clone.clone();
76
77 glib::MainContext::default().spawn_local(async move {
78 if let Ok(nm) = NetworkManager::new().await {
79 if nm.forget(&ssid).await.is_ok() {
80 stack.set_visible_child_name("networks");
81 if let Some(callback) = on_success.borrow().as_ref() {
82 callback();
83 }
84 }
85 }
86 });
87 });
88 }
89
90 header.append(&icon);
91 header.append(&title);
92 header.append(&spacer);
93 header.append(&forget_btn);
94 root.append(&header);
95
96 let basic_box = Box::new(Orientation::Vertical, 6);
97 basic_box.add_css_class("basic-section");
98
99 let basic_header = Label::new(Some("Basic"));
100 basic_header.add_css_class("section-header");
101 basic_box.append(&basic_header);
102
103 let status = Label::new(None);
104 let strength = Label::new(None);
105 let bars = Label::new(None);
106
107 Self::add_row(&basic_box, "Connection Status", &status);
108 Self::add_row(&basic_box, "Signal Strength", &strength);
109 Self::add_row(&basic_box, "Bars", &bars);
110
111 root.append(&basic_box);
112
113 let advanced_box = Box::new(Orientation::Vertical, 8);
114 advanced_box.add_css_class("advanced-section");
115
116 let advanced_header = Label::new(Some("Advanced"));
117 advanced_header.add_css_class("section-header");
118 advanced_box.append(&advanced_header);
119
120 let bssid = Label::new(None);
121 let freq = Label::new(None);
122 let channel = Label::new(None);
123 let mode = Label::new(None);
124 let rate = Label::new(None);
125 let security = Label::new(None);
126
127 Self::add_row(&advanced_box, "BSSID", &bssid);
128 Self::add_row(&advanced_box, "Frequency", &freq);
129 Self::add_row(&advanced_box, "Channel", &channel);
130 Self::add_row(&advanced_box, "Mode", &mode);
131 Self::add_row(&advanced_box, "Speed", &rate);
132 Self::add_row(&advanced_box, "Security", &security);
133
134 root.append(&advanced_box);
135
136 Self {
137 root,
138 title,
139 status,
140 strength,
141 bars,
142
143 bssid,
144 freq,
145 channel,
146 mode,
147 rate,
148 security,
149 current_ssid,
150 on_success: on_success_callback,
151 }
152 }
153
154 pub fn set_on_success(&self, callback: Rc<dyn Fn()>) {
155 *self.on_success.borrow_mut() = Some(callback);
156 }
157
158 fn add_row(parent: >k::Box, key_text: &str, val_widget: >k::Label) {
159 let row = Box::new(Orientation::Vertical, 3);
160 row.set_halign(Align::Start);
161
162 let key = Label::new(Some(key_text));
163 key.add_css_class("info-label");
164 key.set_halign(Align::Start);
165
166 val_widget.add_css_class("info-value");
167 val_widget.set_halign(Align::Start);
168
169 row.append(&key);
170 row.append(val_widget);
171 parent.append(&row);
172 }
173
174 pub fn update(&self, info: &NetworkInfo) {
175 self.current_ssid.replace(info.ssid.clone());
176 self.title.set_text(&info.ssid);
177 self.status.set_text(&info.status);
178 self.strength.set_text(&format!("{}%", info.strength));
179 self.bars.set_text(&info.bars);
180
181 self.bssid.set_text(&info.bssid);
182 self.freq.set_text(
183 &info
184 .freq
185 .map(|f| format!("{:.1} GHz", f as f32 / 1000.0))
186 .unwrap_or_else(|| "-".into()),
187 );
188 self.channel.set_text(
189 &info
190 .channel
191 .map(|c| c.to_string())
192 .unwrap_or_else(|| "-".into()),
193 );
194 self.mode.set_text(&info.mode);
195 self.rate.set_text(
196 &info
197 .rate_mbps
198 .map(|r| format!("{r:.2} Mbps"))
199 .unwrap_or_else(|| "-".into()),
200 );
201 self.security.set_text(&info.security);
202 }
203
204 pub fn widget(&self) -> >k::Box {
205 &self.root
206 }
207}