azalea_shell/window/taskbar/widget/startmenu/
mod.rs1use azalea_service::{LocalListenerHandle, StaticServiceManager};
2use gtk::prelude::*;
3use relm4::{
4 Component, ComponentController, ComponentParts, ComponentSender, SimpleComponent, component,
5};
6
7use crate::{
8 component::{image, login},
9 service,
10};
11
12crate::init! {
13 Model {
14 taskbar_image: relm4::Controller<image::Model>,
15 popup_image: relm4::Controller<image::Model>,
16 login_widget: relm4::Controller<login::Model>,
17 sysinfo: SystemInformation,
18 temperature: (f64, String),
19 _service_handle: LocalListenerHandle,
20 }
21
22 Config {
23 }
24}
25
26struct SystemInformation {
28 os_name: String,
29 username: String,
30 hostname: String,
31 kernel: String,
32 cpu: String,
33 gpu: String,
34 memory: String,
35 compositor: String,
36}
37
38impl Default for SystemInformation {
39 fn default() -> Self {
40 Self {
41 os_name: ffetch::get_os_name().unwrap_or("unknown".to_string()),
42 kernel: ffetch::get_kernel_version().unwrap_or("unknown".to_string()),
43 username: ffetch::get_username(),
44 hostname: ffetch::get_hostname().unwrap_or("unknown".to_string()),
45 cpu: ffetch::get_cpu_name().unwrap_or("unknown".to_string()),
46 gpu: ffetch::get_gpu(),
47 memory: ffetch::get_memory().unwrap_or("unknown".to_string()),
48 compositor: ffetch::get_desktop_env(),
49 }
50 }
51}
52
53#[derive(Debug)]
54pub enum Input {
55 Weather(service::weather::Output),
56}
57
58#[derive(Debug)]
59pub enum CommandOutput {}
60
61#[component(pub)]
62impl SimpleComponent for Model {
63 type Init = Init;
64 type Input = Input;
65 type Output = ();
66
67 view! {
68 gtk::MenuButton {
69 #[wrap(Some)]
70 set_child= >k::Box {
71 set_spacing: 12,
72
73 #[local_ref]
74 taskbar_image_widget -> gtk::Widget {
75 set_height_request: 30,
76 },
77
78 gtk::Label {
79 set_label: "Azalea"
80 },
81 },
82
83 #[wrap(Some)]
84 set_popover = >k::Popover {
85 gtk::Box {
86 gtk::Box {
87 set_orientation: gtk::Orientation::Vertical,
88
89 #[local_ref]
90 popup_image_widget -> gtk::Widget {
91 },
92
93 gtk::Separator {},
94
95 #[local_ref]
96 login_widget -> gtk::Widget {},
97
98 gtk::Separator {},
99
100 gtk::Box {
101 set_spacing: 8,
102 set_halign: gtk::Align::Start,
103 gtk::Label {
104 set_css_classes: &[ "azalea-primary-fg", ],
105 set_label: &format!("{}@{}", model.sysinfo.username, model.sysinfo.hostname),
106 },
107 },
108
109 gtk::Box {
110 set_spacing: 8,
111 set_halign: gtk::Align::Start,
112 gtk::Label {
113 set_css_classes: &[ "azalea-secondary-fg", ],
114 set_label: " OS:",
115 },
116 gtk::Label {
117 set_label: &model.sysinfo.os_name,
118 },
119 },
120
121 gtk::Box {
122 set_spacing: 8,
123 set_halign: gtk::Align::Start,
124 gtk::Label {
125 set_css_classes: &[ "azalea-tertiary-fg", ],
126 set_label: " Kernel:",
127 },
128 gtk::Label {
129 set_label: &model.sysinfo.kernel,
130 },
131 },
132
133 gtk::Box {
134 set_spacing: 8,
135 set_halign: gtk::Align::Start,
136 gtk::Label {
137 set_css_classes: &[ "azalea-primary-fg", ],
138 set_label: " CPU:",
139 },
140 gtk::Label {
141 set_label: &model.sysinfo.cpu,
142 },
143 },
144
145 gtk::Box {
146 set_spacing: 8,
147 set_halign: gtk::Align::Start,
148 gtk::Label {
149 set_css_classes: &[ "azalea-secondary-fg", ],
150 set_label: " GPU:",
151 },
152 gtk::Label {
153 set_label: &model.sysinfo.gpu,
154 },
155 },
156
157 gtk::Box {
158 set_spacing: 8,
159 set_halign: gtk::Align::Start,
160 gtk::Label {
161 set_css_classes: &[ "azalea-tertiary-fg", ],
162 set_label: " Memory:",
163 },
164 gtk::Label {
165 set_label: &model.sysinfo.memory,
166 },
167 },
168
169 gtk::Box {
170 set_spacing: 8,
171 set_halign: gtk::Align::Start,
172 gtk::Label {
173 set_css_classes: &[ "azalea-secondary-fg", ],
174 set_label: " Compositor:",
175 },
176 gtk::Label {
177 set_label: &model.sysinfo.compositor,
178 },
179 },
180 },
181 }
182 }
183 }
184 }
185
186 fn init(
187 _init: Self::Init,
188 _root: Self::Root,
189 sender: ComponentSender<Self>,
190 ) -> ComponentParts<Self> {
191 let model = Model {
192 temperature: Default::default(),
193 sysinfo: Default::default(),
194 taskbar_image: image::Model::builder()
195 .launch(image::Init {
196 fallback: None,
197 width: None,
198 height: Some(30),
199 })
200 .detach(),
201 popup_image: image::Model::builder()
202 .launch(image::Init {
203 fallback: None,
204 width: None,
205 height: Some(200),
206 })
207 .detach(),
208 login_widget: login::Model::builder().launch(()).detach(),
209 _service_handle: service::weather::Service::forward_local(
210 sender.input_sender().clone(),
211 Input::Weather,
212 ),
213 };
214
215 drop(model.taskbar_image.sender().send(image::Input::LoadBytes(
216 include_bytes!("../../../../../../assets/azalea-logo.png").to_vec(),
217 )));
218
219 drop(model.popup_image.sender().send(image::Input::LoadBytes(
220 include_bytes!("../../../../../../assets/azalea-logo.png").to_vec(),
221 )));
222
223 let taskbar_image_widget: >k::Widget = model.taskbar_image.widget().upcast_ref();
224 let popup_image_widget: >k::Widget = model.popup_image.widget().upcast_ref();
225 let login_widget: >k::Widget = model.login_widget.widget().upcast_ref();
226 let widgets = view_output!();
227
228 ComponentParts { model, widgets }
229 }
230
231 fn update(&mut self, message: Self::Input, _sender: ComponentSender<Self>) {
232 match message {
233 Input::Weather(weather) => match weather {
234 service::weather::Output::Temperature(temperature) => {
235 self.temperature = temperature;
236 }
237 },
238 }
239 }
240}