nmrs_gui/ui/
wired_devices.rs1use gtk::prelude::*;
2use gtk::Align;
3use gtk::GestureClick;
4use gtk::{Box, Image, Label, ListBox, ListBoxRow, Orientation};
5use nmrs::{models, NetworkManager};
6use std::rc::Rc;
7
8use crate::ui::wired_page::WiredPage;
9
10pub struct WiredDeviceRowController {
11 pub row: gtk::ListBoxRow,
12 pub arrow: gtk::Image,
13 pub ctx: Rc<WiredDevicesContext>,
14 pub device: models::Device,
15 pub details_page: Rc<WiredPage>,
16}
17
18pub struct WiredDevicesContext {
19 pub nm: Rc<NetworkManager>,
20 pub on_success: Rc<dyn Fn()>,
21 pub status: Label,
22 pub stack: gtk::Stack,
23 pub parent_window: gtk::ApplicationWindow,
24}
25
26impl WiredDeviceRowController {
27 pub fn new(
28 row: gtk::ListBoxRow,
29 arrow: gtk::Image,
30 ctx: Rc<WiredDevicesContext>,
31 device: models::Device,
32 details_page: Rc<WiredPage>,
33 ) -> Self {
34 Self {
35 row,
36 arrow,
37 ctx,
38 device,
39 details_page,
40 }
41 }
42
43 pub fn attach(&self) {
44 self.attach_arrow();
45 self.attach_row_double();
46 }
47
48 fn attach_arrow(&self) {
49 let click = GestureClick::new();
50
51 let device = self.device.clone();
52 let stack = self.ctx.stack.clone();
53 let page = self.details_page.clone();
54
55 click.connect_pressed(move |_, _, _, _| {
56 let device_c = device.clone();
57 let stack_c = stack.clone();
58 let page_c = page.clone();
59
60 glib::MainContext::default().spawn_local(async move {
61 page_c.update(&device_c);
62 stack_c.set_visible_child_name("wired-details");
63 });
64 });
65
66 self.arrow.add_controller(click);
67 }
68
69 fn attach_row_double(&self) {
70 let click = GestureClick::new();
71
72 let ctx = self.ctx.clone();
73 let device = self.device.clone();
74 let interface = device.interface.clone();
75
76 let status = ctx.status.clone();
77 let window = ctx.parent_window.clone();
78 let on_success = ctx.on_success.clone();
79
80 click.connect_pressed(move |_, n, _, _| {
81 if n != 2 {
82 return;
83 }
84
85 status.set_text(&format!("Connecting to {interface}..."));
86
87 let nm_c = ctx.nm.clone();
88 let status_c = status.clone();
89 let window_c = window.clone();
90 let on_success_c = on_success.clone();
91
92 glib::MainContext::default().spawn_local(async move {
93 window_c.set_sensitive(false);
94 match nm_c.connect_wired().await {
95 Ok(_) => {
96 status_c.set_text("");
97 on_success_c();
98 }
99 Err(e) => status_c.set_text(&format!("Failed to connect: {e}")),
100 }
101 window_c.set_sensitive(true);
102 status_c.set_text("");
103 });
104 });
105
106 self.row.add_controller(click);
107 }
108}
109
110pub fn wired_devices_view(
111 ctx: Rc<WiredDevicesContext>,
112 devices: &[models::Device],
113 details_page: Rc<WiredPage>,
114) -> ListBox {
115 let list = ListBox::new();
116
117 for device in devices {
118 let row = ListBoxRow::new();
119 let hbox = Box::new(Orientation::Horizontal, 6);
120
121 row.add_css_class("network-selection");
122
123 if device.state == models::DeviceState::Activated {
124 row.add_css_class("connected");
125 }
126
127 let display_name = format!("{} ({})", device.interface, device.device_type);
128 hbox.append(&Label::new(Some(&display_name)));
129
130 if device.state == models::DeviceState::Activated {
131 let connected_label = Label::new(Some("Connected"));
132 connected_label.add_css_class("connected-label");
133 hbox.append(&connected_label);
134 }
135
136 let spacer = Box::new(Orientation::Horizontal, 0);
137 spacer.set_hexpand(true);
138 hbox.append(&spacer);
139
140 let state_text = match device.state {
142 models::DeviceState::Activated => Some("Connected"),
143 models::DeviceState::Disconnected => Some("Disconnected"),
144 models::DeviceState::Unavailable => Some("Unavailable"),
145 models::DeviceState::Failed => Some("Failed"),
146 _ => None,
148 };
149
150 if let Some(text) = state_text {
151 let state_label = Label::new(Some(text));
152 state_label.add_css_class(match device.state {
153 models::DeviceState::Activated => "network-good",
154 models::DeviceState::Unavailable
155 | models::DeviceState::Disconnected
156 | models::DeviceState::Failed => "network-poor",
157 _ => "network-okay",
158 });
159 hbox.append(&state_label);
160 }
161
162 let icon = Image::from_icon_name("network-wired-symbolic");
163 icon.add_css_class("wired-icon");
164 hbox.append(&icon);
165
166 let arrow = Image::from_icon_name("go-next-symbolic");
167 arrow.set_halign(Align::End);
168 arrow.add_css_class("network-arrow");
169 arrow.set_cursor_from_name(Some("pointer"));
170 hbox.append(&arrow);
171
172 row.set_child(Some(&hbox));
173
174 let controller = WiredDeviceRowController::new(
175 row.clone(),
176 arrow.clone(),
177 ctx.clone(),
178 device.clone(),
179 details_page.clone(),
180 );
181
182 controller.attach();
183
184 list.append(&row);
185 }
186 list
187}