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

1use gtk::{gio, prelude::*};
2use relm4::{Component, ComponentParts, ComponentSender, component};
3
4use crate::{icon, service::search::AppInfo};
5
6crate::init! {
7    Model {
8        icon: Option<gio::Icon>,
9        name: Option<String>,
10        app_info: Option<gio::AppInfo>,
11    }
12
13    Config {
14        desktop_entry: String,
15    }
16}
17
18#[derive(Debug)]
19pub enum Input {
20    Click,
21}
22
23#[derive(Debug)]
24pub enum CommandOutput {
25    FindApplication(Vec<AppInfo>),
26}
27
28#[component(pub)]
29impl Component for Model {
30    type Init = Init;
31    type Input = Input;
32    type Output = ();
33    type CommandOutput = CommandOutput;
34
35    view! {
36        gtk::Button {
37            gtk::Box {
38                set_spacing: 8,
39
40
41                gtk::Image {
42                    set_from_gicon: model.icon
43                        .as_ref()
44                        .unwrap_or(&gio::ThemedIcon::from_names(&[icon::APPS]).upcast::<gio::Icon>())
45                },
46
47                gtk::Label {
48                    set_label: &model.name.as_deref().unwrap_or(""),
49                }
50            },
51
52            connect_clicked => Input::Click,
53        }
54    }
55
56    fn init(
57        init: Self::Init,
58        _root: Self::Root,
59        sender: ComponentSender<Self>,
60    ) -> ComponentParts<Self> {
61        let app = gio::AppInfo::all().into_iter().find(|app| {
62            app.id().map(|id| id.to_string()).unwrap_or_default() == init.config.desktop_entry
63        });
64
65        let model = Model {
66            icon: app.as_ref().and_then(|app| app.icon()),
67            name: app.as_ref().map(|app| app.name().to_string()),
68            app_info: app,
69        };
70
71        if model.app_info.is_none() {
72            azalea_log::warning!(Self, "Failed to find desktop entry");
73        }
74
75        let widgets = view_output!();
76
77        ComponentParts { model, widgets }
78    }
79
80    fn update(&mut self, message: Self::Input, _sender: ComponentSender<Self>, _root: &Self::Root) {
81        match message {
82            Input::Click => {
83                if let Some(app_info) = &self.app_info {
84                    let executable = app_info.executable();
85                    match std::process::Command::new(&executable)
86                        .stdin(std::process::Stdio::null())
87                        .stdout(std::process::Stdio::null())
88                        .stderr(std::process::Stdio::null())
89                        .spawn()
90                    {
91                        Ok(_) => azalea_log::debug!("Launched application: {:?}", executable),
92                        Err(e) => azalea_log::warning!(
93                            Self,
94                            "Failed to launch application {:?}: {e}",
95                            executable
96                        ),
97                    }
98                }
99            }
100        }
101    }
102
103    fn update_cmd(
104        &mut self,
105        message: Self::CommandOutput,
106        _sender: ComponentSender<Self>,
107        _root: &Self::Root,
108    ) {
109        match message {
110            CommandOutput::FindApplication(app_infos) => {
111                for app_info in app_infos {
112                    println!("{app_info:#?}");
113                }
114            }
115        }
116    }
117}