use gtk4 as gtk; use std::{collections::VecDeque, sync::LazyLock}; use gtk::{ builders::{BoxBuilder, ButtonBuilder, LabelBuilder}, prelude::{BoxExt, ButtonExt, ObjectExt, WidgetExt}, Box, Button, Label, Revealer, RevealerTransitionType, }; use crate::model::builder_traits::Product; use tokio::{ task::{spawn, JoinHandle}, time::{sleep, Duration}, }; #[derive(Clone)] pub struct InfoBar { instance: Revealer, } pub struct InfoBarBuilder { label: LabelBuilder, r#box: BoxBuilder, button: ButtonBuilder, } static mut TASKS_QUEUE: VecDeque> = VecDeque::new(); // // Singleton pattern implementation // unsafe impl Send for InfoBar {} unsafe impl Sync for InfoBar {} static INFO_BAR_INSTANCE: LazyLock = LazyLock::new(|| InfoBar { instance: Revealer::new(), }); impl InfoBar { pub fn get_instance() -> &'static Self { &INFO_BAR_INSTANCE } pub fn set_text_label(&self, text_label: Option<&str>) { if let Some(text) = text_label { let _ = &INFO_BAR_INSTANCE .instance .child() .unwrap() .first_child() .unwrap() .set_property("label", text); } } pub fn set_reveal_child(&self, is_reveal: bool) { let _ = &INFO_BAR_INSTANCE.instance.set_reveal_child(is_reveal); } pub fn show_infobar(&self, duration_secs: u64) { if !INFO_BAR_INSTANCE.instance.reveals_child() { INFO_BAR_INSTANCE.set_reveal_child(true); } let callback = spawn(async move { sleep(Duration::from_secs(duration_secs)).await; INFO_BAR_INSTANCE.set_reveal_child(false); }); unsafe { if !TASKS_QUEUE.is_empty() { TASKS_QUEUE.pop_back().unwrap().abort(); } TASKS_QUEUE.push_back(callback); } } } // // Builder pattern implementation // impl InfoBarBuilder { pub fn build(self) -> &'static InfoBar { let info_bar_label = self.label.build(); let info_bar_box = self.r#box.build(); let info_bar_close_btn = self.button.build(); info_bar_label.set_hexpand(true); info_bar_box.append(&info_bar_label); info_bar_box.append(&info_bar_close_btn); info_bar_box.set_widget_name("info_bar"); let info_bar: &Revealer = &INFO_BAR_INSTANCE.instance; info_bar.set_transition_type(RevealerTransitionType::SlideUp); info_bar.set_transition_duration(200); info_bar.set_child(Some(&info_bar_box)); let info_bar_to_close = info_bar.clone(); info_bar_close_btn.connect_clicked(move |_| { info_bar_to_close.set_reveal_child(false); }); &INFO_BAR_INSTANCE } pub fn set_text_label(mut self, text: &str) -> Self { self.label = self.label.label(text); self } pub fn set_button_icon(mut self, icon_name: &str) -> Self { self.button = self.button.icon_name(icon_name); self } } impl Product for InfoBar { fn builder() -> InfoBarBuilder { InfoBarBuilder { label: Label::builder(), r#box: Box::builder(), button: Button::builder(), } } fn get(&self) -> &'static Revealer { &INFO_BAR_INSTANCE.instance } }