ferroui_native/
tray_icon_impl.rs1use crate::ferro_native_menu_exporter::FerroNativeMenuExporter;
2use crate::frn_string::to_c_string;
3use crate::helpers::ComResultExt;
4use crate::interop::*;
5use ferroui_base::reactive::IDisposable;
6use ferroui_controls::platform::{INativeMenuExporter, ITrayIconImpl, ITrayIconWithIsTemplateImpl, IWindowIconImpl};
7use ferroui_microcom::ComPtr;
8use std::cell::RefCell;
9use std::ffi::c_void;
10use std::rc::Rc;
11
12pub struct TrayIconImpl {
14 native: RefCell<Option<ComPtr<IFrnTrayIcon>>>,
15 menu_exporter: Rc<FerroNativeMenuExporter>,
16 on_clicked: RefCell<Option<Rc<dyn Fn()>>>,
17}
18
19impl TrayIconImpl {
20 pub(crate) fn new(factory: &ComPtr<IFerroNativeFactory>) -> TrayIconImpl {
21 let native = factory.create_tray_icon().check().expect("the native tray icon");
22
23 TrayIconImpl {
24 menu_exporter: FerroNativeMenuExporter::for_tray_icon(native.clone(), factory),
25 native: RefCell::new(Some(native)),
26 on_clicked: RefCell::new(None),
27 }
28 }
29
30 #[track_caller]
31 fn native(&self) -> ComPtr<IFrnTrayIcon> {
32 match self.native.borrow().clone() {
33 Some(native) => native,
34 None => panic!("Cannot access a disposed object: the native tray icon"),
35 }
36 }
37}
38
39impl IDisposable for TrayIconImpl {
40 fn dispose(&self) {
41 let native = self.native.borrow_mut().take();
42 drop(native);
43 }
44}
45
46impl ITrayIconImpl for TrayIconImpl {
47 fn set_icon(&self, icon: Option<Rc<dyn IWindowIconImpl>>) {
48 match icon {
49 None => unsafe { self.native().set_icon(std::ptr::null_mut(), 0) }.check(),
51 Some(icon) => {
52 let mut image_data = Vec::new();
53 if let Err(error) = icon.save(&mut image_data) {
54 panic!("Unable to save the tray icon: {error}");
55 }
56
57 unsafe { self.native().set_icon(image_data.as_mut_ptr() as *mut c_void, image_data.len()) }.check();
59 }
60 }
61 }
62
63 fn set_tool_tip_text(&self, text: Option<&str>) {
64 let text = text.map(to_c_string);
65 self.native().set_tool_tip_text(text.as_deref()).check();
66 }
67
68 fn set_is_visible(&self, visible: bool) {
69 self.native().set_is_visible(visible).check();
70 }
71
72 fn menu_exporter(&self) -> Option<Rc<dyn INativeMenuExporter>> {
73 Some(self.menu_exporter.clone())
74 }
75
76 fn on_clicked(&self) -> Option<Rc<dyn Fn()>> {
77 self.on_clicked.borrow().clone()
78 }
79
80 fn set_on_clicked(&self, value: Option<Rc<dyn Fn()>>) {
81 let old = self.on_clicked.replace(value);
82 drop(old);
83 }
84
85 fn as_tray_icon_with_is_template_impl(&self) -> Option<&dyn ITrayIconWithIsTemplateImpl> {
86 Some(self)
87 }
88}
89
90impl ITrayIconWithIsTemplateImpl for TrayIconImpl {
91 fn set_is_template_icon(&self, is_template_icon: bool) {
92 self.native().set_is_template_icon(is_template_icon).check();
93 }
94}