azalea_shell/window/taskbar/widget/network/
mod.rs

1use azalea_service::{LocalListenerHandle, StaticServiceManager};
2use gtk::prelude::*;
3use relm4::{
4    Component, ComponentParts, ComponentSender, RelmWidgetExt, component, prelude::FactoryVecDeque,
5};
6
7use crate::{
8    factory, icon,
9    service::dbus::network_manager::{
10        self,
11        proxy::{NMConnectivityState, NMState},
12    },
13};
14
15crate::init! {
16    Model {
17        enabled: bool,
18        state_connectivity: (NMState, NMConnectivityState),
19        _nm_handle: LocalListenerHandle,
20        devices_menu: FactoryVecDeque<factory::network::device::Model>,
21        connections_menu: FactoryVecDeque<factory::network::connection::Model>,
22    }
23
24    Config {}
25}
26
27#[derive(Debug)]
28pub enum Input {
29    Enable(bool),
30    NetworkManager(network_manager::Output),
31}
32
33#[component(pub)]
34impl Component for Model {
35    type Init = Init;
36    type Input = Input;
37    type Output = ();
38    type CommandOutput = ();
39
40    view! {
41        gtk::MenuButton {
42            set_hexpand: false,
43            set_vexpand: false,
44            set_valign: gtk::Align::Center,
45
46            #[watch]
47            set_icon_name: match model.state_connectivity {
48                (NMState::NMStateUnknown        , _) => icon::WIFI_QUESTION_MARK,
49                (NMState::NMStateAsleep         , _)=> icon::WIFI_SLEEP,
50                (NMState::NMStateDisconnected   , _)=> icon::WIFI_X,
51                (NMState::NMStateDisconnecting  , _)=> icon::WIFI_DOTS,
52                (NMState::NMStateConnecting     , _)=> icon::WIFI_DOTS,
53                (NMState::NMStateConnectedLocal , _)=> icon::WIFI_NONE,
54                (NMState::NMStateConnectedSite  , _)=> icon::WIFI_NONE,
55                (NMState::NMStateConnectedGlobal, NMConnectivityState::NMConnectivityFull)=> icon::WIFI_3,
56                (NMState::NMStateConnectedGlobal, NMConnectivityState::NMConnectivityLimited)=> icon::WIFI_2,
57                (NMState::NMStateConnectedGlobal, _)=> icon::WIFI_0,
58            },
59
60            #[wrap(Some)]
61            set_popover = &gtk::Popover {
62                set_position: gtk::PositionType::Right,
63
64                gtk::Box {
65                    set_spacing: 12,
66                    set_orientation: gtk::Orientation::Vertical,
67
68                    gtk::Box {
69                        set_spacing: 12,
70
71                        gtk::Label::new(Some("Network")) {
72                            inline_css: r#"
73                                font-weight: bold;
74                            "#,
75
76                            #[watch]
77                            set_css_classes: if model.enabled {
78                                &[ "azalea-primary-fg" ]
79                            } else {
80                                &[]
81                            },
82                            set_halign: gtk::Align::Start,
83                            set_hexpand: true,
84                        },
85
86                        gtk::Switch {
87                            set_halign: gtk::Align::End,
88
89                            #[watch]
90                            #[block_signal(toggle_state)]
91                            set_active: model.enabled,
92
93                            connect_state_set[sender] => move |_, on| {
94                                sender.input(Input::Enable(on));
95                                false.into()
96                            } @toggle_state,
97                        },
98                    },
99
100                    // This isn't particularly useful (at least for me)
101                    // gtk::Separator {},
102                    //
103                    // gtk::Label::new(Some("Devices")) {
104                    //     set_css_classes: &[ "azalea-primary-fg" ],
105                    //     set_halign: gtk::Align::Start,
106                    //     set_hexpand: true,
107                    // },
108                    //
109                    // #[local_ref]
110                    // devices_widget -> gtk::Box {
111                    //     set_orientation: gtk::Orientation::Vertical,
112                    //     set_spacing: 5,
113                    // },
114
115                    gtk::Separator {},
116
117                    gtk::Label::new(Some("Connections")) {
118                        set_css_classes: &[ "azalea-primary-fg" ],
119                        set_halign: gtk::Align::Start,
120                        set_hexpand: true,
121                    },
122
123                    #[local_ref]
124                    connections_widget -> gtk::Box {
125                        set_orientation: gtk::Orientation::Vertical,
126                        set_spacing: 5,
127                    }
128                },
129            },
130        },
131    }
132
133    fn init(
134        _init: Self::Init,
135        _root: Self::Root,
136        sender: ComponentSender<Self>,
137    ) -> ComponentParts<Self> {
138        let model = Model {
139            enabled: true,
140            state_connectivity: Default::default(),
141            _nm_handle: network_manager::Service::forward_local(
142                sender.input_sender().clone(),
143                Input::NetworkManager,
144            ),
145            devices_menu: FactoryVecDeque::builder()
146                .launch(gtk::Box::default())
147                .detach(),
148            connections_menu: FactoryVecDeque::builder()
149                .launch(gtk::Box::default())
150                .detach(),
151        };
152
153        network_manager::Service::send(network_manager::Input::Update);
154
155        let connections_widget = model.connections_menu.widget();
156        let widgets = view_output!();
157
158        ComponentParts { model, widgets }
159    }
160
161    fn update(&mut self, message: Self::Input, _sender: ComponentSender<Self>, _root: &Self::Root) {
162        use network_manager::Output;
163        match message {
164            Input::NetworkManager(nm_msg) => match nm_msg {
165                Output::NetworkingEnabledChanged(on) => self.enabled = on,
166                Output::StateChanged(nmstate) => self.state_connectivity.0 = nmstate,
167                Output::ConnectivityChanged(nmconnectivity_state) => {
168                    self.state_connectivity.1 = nmconnectivity_state
169                }
170                Output::Devices(devices) => {
171                    let mut guard = self.devices_menu.guard();
172
173                    guard.clear();
174
175                    for device in devices {
176                        guard.push_back(device);
177                    }
178                }
179                Output::Connections(connections) => {
180                    let mut guard = self.connections_menu.guard();
181
182                    guard.clear();
183
184                    for connection in connections {
185                        guard.push_back(connection);
186                    }
187                }
188            },
189            Input::Enable(on) => {
190                network_manager::Service::send(network_manager::Input::Enable(on));
191            }
192        }
193    }
194
195    fn update_cmd(
196        &mut self,
197        _message: Self::CommandOutput,
198        _sender: ComponentSender<Self>,
199        _root: &Self::Root,
200    ) {
201    }
202}