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 gtk::{
    ListItem,
    glib::{self},
    subclass::prelude::*,
};

use crate::widget::unit_properties_selector::{
    data_selection::UnitPropertySelection, unit_properties_selection::UnitPropertiesSelectionPanel,
};

glib::wrapper! {
    pub struct UnitPropertiesSelectionRow(ObjectSubclass<imp::UnitPropertiesSelectionRowImp>)
    @extends gtk::Grid, gtk::Widget,
    @implements gtk::Actionable, gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Orientable;
}

impl UnitPropertiesSelectionRow {
    pub fn new(prop_selection: UnitPropertiesSelectionPanel) -> Self {
        let obj: UnitPropertiesSelectionRow = glib::Object::new();

        obj.imp().prop_selection.replace(Some(prop_selection));

        obj
    }

    pub fn set_data_selection(&self, prop_seletion: &UnitPropertySelection, list_item: &ListItem) {
        self.imp().set_data_selection(prop_seletion, list_item)
    }

    pub fn unbind(&self) {
        self.imp().unbind()
    }
}

impl Default for UnitPropertiesSelectionRow {
    fn default() -> Self {
        glib::Object::new()
    }
}

mod imp {
    use std::cell::RefCell;

    use crate::widget::unit_properties_selector::{
        data_selection::UnitPropertySelection,
        unit_properties_selection::UnitPropertiesSelectionPanel,
    };

    use super::UnitPropertiesSelectionRow;
    use gio::glib::Binding;
    use glib::GString;
    use gtk::{
        glib::{self},
        prelude::*,
        subclass::prelude::*,
    };
    use tracing::warn;

    #[derive(Default, gtk::CompositeTemplate)]
    #[template(resource = "/io/github/plrigaux/sysd-manager/unit_properties_selection_row.ui")]
    pub struct UnitPropertiesSelectionRowImp {
        #[template_child]
        interface: TemplateChild<gtk::Label>,
        #[template_child]
        property_label: TemplateChild<gtk::Label>,
        #[template_child]
        hidden_ckeck: TemplateChild<gtk::CheckButton>,
        #[template_child]
        title_entry: TemplateChild<gtk::Entry>,
        #[template_child]
        width_spin: TemplateChild<gtk::SpinButton>,
        #[template_child]
        resizable_switch: TemplateChild<gtk::Switch>,
        #[template_child]
        expand_switch: TemplateChild<gtk::Switch>,

        pub prop_selection: RefCell<Option<UnitPropertiesSelectionPanel>>,

        list_item: RefCell<Option<gtk::ListItem>>,

        binds: RefCell<Vec<Binding>>,
    }

    #[gtk::template_callbacks]
    impl UnitPropertiesSelectionRowImp {
        pub(super) fn set_data_selection(
            &self,
            prop_selection: &UnitPropertySelection,
            list_item: &gtk::ListItem,
        ) {
            self.list_item.replace(Some(list_item.clone()));

            let interface = prop_selection.interface();

            if let Some(token) = interface.split('.').next_back() {
                self.interface.set_label(token);
            } else {
                self.interface.set_label(&interface);
            }

            self.property_label
                .set_label(&prop_selection.unit_property());

            self.hidden_ckeck.set_active(prop_selection.visible());
            let bind = self
                .hidden_ckeck
                .bind_property("active", prop_selection, "visible")
                .build();

            self.binds.borrow_mut().push(bind);

            let title = prop_selection.title().unwrap_or_default();
            self.title_entry.buffer().set_text(title);
            let bind = self
                .title_entry
                .buffer()
                .bind_property("text", prop_selection, "title")
                .transform_to(|_b, s: GString| Some(s))
                .build();

            self.binds.borrow_mut().push(bind);

            self.width_spin
                .set_value(prop_selection.fixed_width() as f64);
            let bind = self
                .width_spin
                .bind_property("value", prop_selection, "fixed-width")
                .transform_to(|_b, v: f64| Some(v as i32))
                .build();

            self.binds.borrow_mut().push(bind);

            self.resizable_switch.set_active(prop_selection.resizable());
            let bind = self
                .resizable_switch
                .bind_property("active", prop_selection, "resizable")
                .build();

            self.binds.borrow_mut().push(bind);

            self.expand_switch.set_active(prop_selection.expands());
            let bind = self
                .expand_switch
                .bind_property("active", prop_selection, "expands")
                .build();

            self.binds.borrow_mut().push(bind);
        }

        pub(super) fn unbind(&self) {
            {
                for b in self.binds.borrow().iter() {
                    b.unbind()
                }
            }

            self.binds.borrow_mut().clear();

            self.list_item.replace(None);
        }
        #[template_callback]
        fn move_up_clicked(&self, _b: &gtk::Button) {
            let Some(ref list_item) = *self.list_item.borrow() else {
                return;
            };

            let pos = list_item.position();
            if pos == 0 {
                return;
            }

            if let Some(list_store) = self
                .prop_selection
                .borrow()
                .as_ref()
                .and_then(|prop_selection| prop_selection.list_store())
                && let Some(ref prop_seletion) = list_store.item(pos)
            {
                list_store.remove(pos);
                list_store.insert(pos - 1, prop_seletion);
            } else {
                warn!("list_store of data None");
            };
        }

        #[template_callback]
        fn move_down_clicked(&self, _b: &gtk::Button) {
            let Some(ref list_item) = *self.list_item.borrow() else {
                return;
            };

            if let Some(prop_selection) = self.prop_selection.borrow().as_ref() {
                let pos = list_item.position();
                prop_selection.move_down(pos);
            }
        }

        #[template_callback]
        fn delete_clicked(&self, _b: &gtk::Button) {
            let Some(ref list_item) = *self.list_item.borrow() else {
                return;
            };

            if let Some(prop_selection) = self.prop_selection.borrow().as_ref() {
                let pos = list_item.position();
                prop_selection.delete(pos);
            }
        }
    }

    // The central trait for subclassing a GObject
    #[glib::object_subclass]
    impl ObjectSubclass for UnitPropertiesSelectionRowImp {
        const NAME: &'static str = "UnitPropertiesSelectorRow";
        type Type = UnitPropertiesSelectionRow;
        type ParentType = gtk::Grid;

        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 UnitPropertiesSelectionRowImp {
        fn constructed(&self) {
            self.parent_constructed();
        }
    }

    impl WidgetImpl for UnitPropertiesSelectionRowImp {}
    impl GridImpl for UnitPropertiesSelectionRowImp {}
}