Skip to main content

nmrs_gui/ui/
network_page.rs

1use glib::clone;
2use gtk::prelude::*;
3use gtk::{Align, Box, Button, Image, Label, Orientation};
4use nmrs::NetworkManager;
5use nmrs::models::NetworkInfo;
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: &gtk::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                        && nm.forget(&ssid).await.is_ok()
80                    {
81                        stack.set_visible_child_name("networks");
82                        if let Some(callback) = on_success.borrow().as_ref() {
83                            callback();
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: &gtk::Box, key_text: &str, val_widget: &gtk::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) -> &gtk::Box {
205        &self.root
206    }
207}