sysd-manager 2.19.4

Application to empower user to manage their <b>systemd units</b> via Graphical User Interface. Not only are you able to make changes to the enablement and running status of each of the units, but you will also be able to view and modify their unit files and check the journal logs.
use crate::widget;

use super::UnitListPanel;
use adw::subclass::prelude::ObjectSubclassIsExt;
use gtk::glib;

glib::wrapper! {
    pub struct UnitListSearchControls(ObjectSubclass<imp::UnitListSearchControlsImp>)
        @extends gtk::Box, gtk::Widget,
        @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Orientable;
}

impl UnitListSearchControls {
    pub fn new(unit_list_panel: &UnitListPanel) -> UnitListSearchControls {
        let obj: UnitListSearchControls = glib::Object::new();
        obj.imp()
            .unit_list_panel
            .set(unit_list_panel.clone())
            .unwrap();
        obj.imp().set_filter_is_set(false);
        obj
    }

    pub fn set_filter_is_set(&self, filter_is_set: bool) {
        self.imp().set_filter_is_set(filter_is_set);
    }

    pub fn grab_focus_on_search_entry(&self) {
        widget::grab_focus_on_search_entry(&self.imp().search_entry);
    }
}

mod imp {
    use super::UnitListSearchControls;
    use crate::{widget, widget::unit_list::UnitListPanel};
    use gtk::{glib, prelude::*, subclass::prelude::*};
    use std::cell::OnceCell;
    use tracing::{debug, error};

    #[derive(Default, gtk::CompositeTemplate)]
    #[template(resource = "/io/github/plrigaux/sysd-manager/unit_list_search.ui")]
    pub struct UnitListSearchControlsImp {
        #[template_child]
        pub(super) search_entry: TemplateChild<gtk::SearchEntry>,

        #[template_child]
        show_filter_button: TemplateChild<gtk::Button>,

        #[template_child]
        clear_filters_button: TemplateChild<gtk::Button>,

        signal_handler_text_changed: OnceCell<glib::SignalHandlerId>,

        pub(super) unit_list_panel: OnceCell<UnitListPanel>,
    }

    #[gtk::template_callbacks]
    impl UnitListSearchControlsImp {
        fn update_unit_name_search(&self, text: &str) {
            let Some(panel) = self.unit_list_panel.get() else {
                error!("unit_list_panel not initialized");
                return;
            };
            panel.imp().update_unit_name_search(text, false)
        }

        pub(crate) fn set_search_entry_text(&self, text: &str) {
            let Some(signal_handler_id) = self.signal_handler_text_changed.get() else {
                error!("signal_handler_text_changed not initialized");
                return;
            };
            self.search_entry.block_signal(signal_handler_id);
            self.search_entry.set_text(text);
            self.search_entry.unblock_signal(signal_handler_id);
        }

        pub(crate) fn clear(&self) {
            self.set_search_entry_text("");
            self.set_filter_is_set(false);
        }

        pub(crate) fn set_filter_is_set(&self, filter_is_set: bool) {
            debug!("FILTER SET {filter_is_set}");
            self.clear_filters_button.set_sensitive(filter_is_set);

            if let Some(child) = self
                .clear_filters_button
                .child()
                .and_downcast::<adw::ButtonContent>()
            {
                child.set_sensitive(filter_is_set);
            }

            if let Some(child) = self
                .show_filter_button
                .child()
                .and_downcast::<adw::ButtonContent>()
            {
                let icon_name = if filter_is_set {
                    "funnel-symbolic"
                } else {
                    "funnel-outline-symbolic"
                };
                child.set_icon_name(icon_name);
            }
        }
    }

    #[glib::object_subclass]
    impl ObjectSubclass for UnitListSearchControlsImp {
        const NAME: &'static str = "SEARCH_CONTROLS";
        type Type = UnitListSearchControls;
        type ParentType = gtk::Box;

        fn class_init(klass: &mut Self::Class) {
            // The layout manager determines how child widgets are laid out.
            klass.bind_template();
            klass.bind_template_callbacks();
        }

        fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
            obj.init_template();
        }
    }

    impl ObjectImpl for UnitListSearchControlsImp {
        fn constructed(&self) {
            self.parent_constructed();

            let unit_list_panel = self.obj().clone();
            let signal_handler_id = self.search_entry.connect_changed(move |entry| {
                let text = entry.text();
                unit_list_panel.imp().update_unit_name_search(text.as_str());
            });

            self.signal_handler_text_changed
                .set(signal_handler_id)
                .expect("Search entry handler set once");

            let event_controller = widget::clear_on_escape();
            self.search_entry.add_controller(event_controller);
        }
    }

    impl WidgetImpl for UnitListSearchControlsImp {}
    impl BoxImpl for UnitListSearchControlsImp {}
}